diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/usage/RetailCustomerEntity.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/usage/RetailCustomerEntity.java
index 57d9e235..73dccd0f 100644
--- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/usage/RetailCustomerEntity.java
+++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/usage/RetailCustomerEntity.java
@@ -28,20 +28,21 @@
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
-import org.greenbuttonalliance.espi.common.domain.common.IdentifiedObject;
import org.greenbuttonalliance.espi.common.utils.security.PasswordPolicy;
import org.hibernate.annotations.BatchSize;
import org.hibernate.proxy.HibernateProxy;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
-import java.util.UUID;
/**
* Pure JPA/Hibernate entity for RetailCustomer without JAXB concerns.
*
* Represents a retail energy customer for Green Button data access.
+ * This is an application-specific correlation table (not part of ESPI standard).
+ * Used to correlate UsagePoint energy data to individual users.
* Authentication concerns are handled in DataCustodian/ThirdParty repositories.
*/
@Entity
@@ -49,13 +50,19 @@
@Getter
@Setter
@NoArgsConstructor
-public class RetailCustomerEntity extends IdentifiedObject {
+public class RetailCustomerEntity implements Serializable {
private static final long serialVersionUID = 1L;
- // Password encoder removed - authentication moved to DataCustodian/ThirdParty
- // private static final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
-
+ /**
+ * Primary key using database auto-increment.
+ * RetailCustomer is an application table, not an ESPI resource.
+ */
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "id")
+ private Long id;
+
// Password policy validator
private static final PasswordPolicy passwordPolicy = new PasswordPolicy();
@@ -288,109 +295,13 @@ public void unlockAccount() {
this.failedLoginAttempts = 0;
}
- /**
- * Generates the self href for this retail customer.
- *
- * @return self href string
- */
- public String getSelfHref() {
- return "/espi/1_1/resource/RetailCustomer/" + getHashedId();
- }
-
- /**
- * Generates the up href for this retail customer.
- *
- * @return up href string
- */
- public String getUpHref() {
- return "/espi/1_1/resource/RetailCustomer";
- }
-
- /**
- * Overrides the default self href generation to use retail customer specific logic.
- *
- * @return self href for this retail customer
- */
- @Override
- protected String generateDefaultSelfHref() {
- return getSelfHref();
- }
- /**
- * Overrides the default up href generation to use retail customer specific logic.
- *
- * @return up href for this retail customer
- */
- @Override
- protected String generateDefaultUpHref() {
- return getUpHref();
- }
-
- /**
- * Manual getter for ID field (Lombok issue workaround).
- *
- * @return the entity ID
- */
- public UUID getId() {
- return this.id;
- }
-
- /**
- * Custom implementation for retail customers to use ID instead of UUID.
- *
- * @return string representation of the ID
- */
- @Override
- public String getHashedId() {
- return getId() != null ? getId().toString() : "";
- }
-
- /**
- * Merges data from another RetailCustomerEntity.
- * Updates user information but preserves security-sensitive fields.
- *
- * @param other the other retail customer entity to merge from
- */
- public void merge(RetailCustomerEntity other) {
- if (other != null) {
- super.merge(other);
-
- // Update basic information
- this.firstName = other.firstName;
- this.lastName = other.lastName;
- this.email = other.email;
- this.phone = other.phone;
-
- // Only update role if provided
- if (other.role != null && !other.role.trim().isEmpty()) {
- this.role = other.role;
- }
-
- // SECURITY: Never merge password, enabled, accountLocked, or authentication fields
- // Password updates must use setPasswordSecurely() method
- // Note: Collections are not merged to preserve existing relationships
- }
- }
-
- /**
- * Safely updates password during merge if a new password is provided.
- * This method should be called separately from merge() for security.
- *
- * @param newRawPassword the new plain text password, or null to skip update
- */
- public void updatePasswordDuringMerge(String newRawPassword) {
- if (newRawPassword != null && !newRawPassword.trim().isEmpty()) {
- setPasswordSecurely(newRawPassword);
- }
- }
/**
* Clears all relationships when unlinking the entity.
- * Simplified - applications handle relationship cleanup.
+ * Applications handle relationship cleanup.
*/
public void unlink() {
- clearRelatedLinks();
-
// Simple collection clearing - applications handle bidirectional cleanup
usagePoints.clear();
authorizations.clear();
@@ -598,22 +509,17 @@ public final int hashCode() {
@Override
public String toString() {
return getClass().getSimpleName() + "(" +
- "id = " + getId() + ", " +
- "username = " + getUsername() + ", " +
- "firstName = " + getFirstName() + ", " +
- "lastName = " + getLastName() + ", " +
- "password = " + getPassword() + ", " +
- "enabled = " + getEnabled() + ", " +
- "role = " + getRole() + ", " +
- "email = " + getEmail() + ", " +
- "phone = " + getPhone() + ", " +
- "accountCreated = " + getAccountCreated() + ", " +
- "lastLogin = " + getLastLogin() + ", " +
- "accountLocked = " + getAccountLocked() + ", " +
- "failedLoginAttempts = " + getFailedLoginAttempts() + ", " +
- "description = " + getDescription() + ", " +
- "created = " + getCreated() + ", " +
- "updated = " + getUpdated() + ", " +
- "published = " + getPublished() + ")";
+ "id = " + id + ", " +
+ "username = " + username + ", " +
+ "firstName = " + firstName + ", " +
+ "lastName = " + lastName + ", " +
+ "enabled = " + enabled + ", " +
+ "role = " + role + ", " +
+ "email = " + email + ", " +
+ "phone = " + phone + ", " +
+ "accountCreated = " + accountCreated + ", " +
+ "lastLogin = " + lastLogin + ", " +
+ "accountLocked = " + accountLocked + ", " +
+ "failedLoginAttempts = " + failedLoginAttempts + ")";
}
}
\ No newline at end of file
diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/usage/UsagePointEntity.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/usage/UsagePointEntity.java
index 127e04a4..55bb6beb 100644
--- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/usage/UsagePointEntity.java
+++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/usage/UsagePointEntity.java
@@ -242,7 +242,7 @@ public String getSelfHref() {
*/
public String getUpHref() {
if (retailCustomer != null) {
- return "RetailCustomer/" + retailCustomer.getHashedId() + "/UsagePoint";
+ return "RetailCustomer/" + retailCustomer.getId() + "/UsagePoint";
}
return "/espi/1_1/resource/UsagePoint";
}
diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/dto/usage/RetailCustomerDto.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/dto/usage/RetailCustomerDto.java
deleted file mode 100644
index a74a2e20..00000000
--- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/dto/usage/RetailCustomerDto.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- *
- * Copyright (c) 2025 Green Button Alliance, Inc.
- *
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package org.greenbuttonalliance.espi.common.dto.usage;
-
-import jakarta.xml.bind.annotation.*;
-import java.util.List;
-
-/**
- * RetailCustomer DTO record for JAXB XML marshalling/unmarshalling.
- *
- * Represents a retail energy customer for Green Button data access.
- * Security-sensitive fields like password are excluded from the DTO.
- */
-@XmlRootElement(name = "RetailCustomer", namespace = "http://naesb.org/espi")
-@XmlAccessorType(XmlAccessType.PROPERTY)
-@XmlType(name = "RetailCustomer", namespace = "http://naesb.org/espi", propOrder = {
- "username", "firstName", "lastName", "email", "phone", "role",
- "enabled", "accountCreated", "lastLogin", "accountLocked",
- "failedLoginAttempts", "usagePoints", "authorizations"
-})
-public record RetailCustomerDto(
-
- String uuid,
- String username,
- String firstName,
- String lastName,
- String email,
- String phone,
- String role,
- Boolean enabled,
- Long accountCreated,
- Long lastLogin,
- Boolean accountLocked,
- Integer failedLoginAttempts,
- List usagePoints,
- List authorizations
-
-) {
-
- /**
- * Gets the full name of the customer.
- *
- * @return formatted full name
- */
- public String getFullName() {
- StringBuilder fullName = new StringBuilder();
- if (firstName != null && !firstName.trim().isEmpty()) {
- fullName.append(firstName.trim());
- }
- if (lastName != null && !lastName.trim().isEmpty()) {
- if (fullName.length() > 0) fullName.append(" ");
- fullName.append(lastName.trim());
- }
- return fullName.toString();
- }
-
- /**
- * Checks if the customer has admin privileges.
- *
- * @return true if role is ROLE_ADMIN, false otherwise
- */
- public boolean isAdmin() {
- return "ROLE_ADMIN".equals(role);
- }
-
- /**
- * Checks if the customer has custodian privileges.
- *
- * @return true if role is ROLE_CUSTODIAN, false otherwise
- */
- public boolean isCustodian() {
- return "ROLE_CUSTODIAN".equals(role);
- }
-
- /**
- * Checks if the customer is a regular user.
- *
- * @return true if role is ROLE_USER, false otherwise
- */
- public boolean isRegularUser() {
- return "ROLE_USER".equals(role);
- }
-
- /**
- * Gets the number of usage points for this customer.
- *
- * @return count of usage points
- */
- public int getUsagePointCount() {
- return usagePoints != null ? usagePoints.size() : 0;
- }
-
- /**
- * Gets the number of authorizations for this customer.
- *
- * @return count of authorizations
- */
- public int getAuthorizationCount() {
- return authorizations != null ? authorizations.size() : 0;
- }
-}
\ No newline at end of file
diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/mapper/usage/RetailCustomerMapper.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/mapper/usage/RetailCustomerMapper.java
deleted file mode 100644
index e6596d1d..00000000
--- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/mapper/usage/RetailCustomerMapper.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- *
- * Copyright (c) 2025 Green Button Alliance, Inc.
- *
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package org.greenbuttonalliance.espi.common.mapper.usage;
-
-import org.greenbuttonalliance.espi.common.domain.usage.RetailCustomerEntity;
-import org.greenbuttonalliance.espi.common.dto.usage.RetailCustomerDto;
-import org.greenbuttonalliance.espi.common.mapper.BaseMapperUtils;
-import org.greenbuttonalliance.espi.common.mapper.DateTimeMapper;
-import org.mapstruct.Mapper;
-import org.mapstruct.Mapping;
-import org.mapstruct.MappingTarget;
-
-/**
- * MapStruct mapper for converting between RetailCustomerEntity and RetailCustomerDto.
- *
- * Handles the conversion between the JPA entity used for persistence and the DTO
- * used for JAXB XML marshalling in the Green Button API.
- * Excludes security-sensitive fields like password from DTO mapping.
- */
-@Mapper(componentModel = "spring", uses = {
- DateTimeMapper.class,
- BaseMapperUtils.class,
- UsagePointMapper.class,
- AuthorizationMapper.class
-})
-public interface RetailCustomerMapper {
-
- /**
- * Converts a RetailCustomerEntity to a RetailCustomerDto.
- * Maps all customer fields except security-sensitive data like password.
- *
- * @param entity the retail customer entity
- * @return the retail customer DTO
- */
- @Mapping(target = "uuid", source = "id", qualifiedByName = "uuidToString")
- @Mapping(target = "username", source = "username")
- @Mapping(target = "firstName", source = "firstName")
- @Mapping(target = "lastName", source = "lastName")
- @Mapping(target = "email", source = "email")
- @Mapping(target = "phone", source = "phone")
- @Mapping(target = "role", source = "role")
- @Mapping(target = "enabled", source = "enabled")
- @Mapping(target = "accountCreated", source = "accountCreated")
- @Mapping(target = "lastLogin", source = "lastLogin")
- @Mapping(target = "accountLocked", source = "accountLocked")
- @Mapping(target = "failedLoginAttempts", source = "failedLoginAttempts")
- @Mapping(target = "usagePoints", source = "usagePoints")
- @Mapping(target = "authorizations", source = "authorizations")
- RetailCustomerDto toDto(RetailCustomerEntity entity);
-
- /**
- * Converts a RetailCustomerDto to a RetailCustomerEntity.
- * Maps customer fields but excludes collections to prevent cascading issues.
- * Security fields like password are not set from DTO for security reasons.
- *
- * @param dto the retail customer DTO
- * @return the retail customer entity
- */
- @Mapping(target = "passwordSecurelyNoValidation", ignore = true)
- @Mapping(target = "passwordSecurely", ignore = true)
- @Mapping(target = "description", ignore = true)
- @Mapping(target = "created", ignore = true)
- @Mapping(target = "id", source = "uuid", qualifiedByName = "stringToUuid")
- @Mapping(target = "username", source = "username")
- @Mapping(target = "firstName", source = "firstName")
- @Mapping(target = "lastName", source = "lastName")
- @Mapping(target = "email", source = "email")
- @Mapping(target = "phone", source = "phone")
- @Mapping(target = "role", source = "role")
- @Mapping(target = "enabled", source = "enabled")
- @Mapping(target = "accountCreated", source = "accountCreated")
- @Mapping(target = "lastLogin", source = "lastLogin")
- @Mapping(target = "accountLocked", source = "accountLocked")
- @Mapping(target = "failedLoginAttempts", source = "failedLoginAttempts")
- @Mapping(target = "password", ignore = true) // Security: never set password from DTO
- @Mapping(target = "usagePoints", ignore = true) // Managed separately
- @Mapping(target = "authorizations", ignore = true) // Managed separately
- @Mapping(target = "published", ignore = true)
- @Mapping(target = "updated", ignore = true)
- RetailCustomerEntity toEntity(RetailCustomerDto dto);
-
- /**
- * Updates an existing RetailCustomerEntity with data from a RetailCustomerDto.
- * Only updates non-security fields and preserves existing relationships.
- *
- * @param dto the source retail customer DTO
- * @param entity the target retail customer entity to update
- */
- @Mapping(target = "passwordSecurelyNoValidation", ignore = true)
- @Mapping(target = "passwordSecurely", ignore = true)
- @Mapping(target = "description", ignore = true)
- @Mapping(target = "created", ignore = true)
- @Mapping(target = "id", ignore = true) // Never change ID
- @Mapping(target = "username", source = "username")
- @Mapping(target = "firstName", source = "firstName")
- @Mapping(target = "lastName", source = "lastName")
- @Mapping(target = "email", source = "email")
- @Mapping(target = "phone", source = "phone")
- @Mapping(target = "role", source = "role")
- @Mapping(target = "enabled", source = "enabled")
- @Mapping(target = "accountCreated", ignore = true) // Preserve original creation time
- @Mapping(target = "lastLogin", source = "lastLogin")
- @Mapping(target = "accountLocked", source = "accountLocked")
- @Mapping(target = "failedLoginAttempts", source = "failedLoginAttempts")
- @Mapping(target = "password", ignore = true) // Security: never update password from DTO
- @Mapping(target = "usagePoints", ignore = true) // Preserve existing relationships
- @Mapping(target = "authorizations", ignore = true) // Preserve existing relationships
- @Mapping(target = "published", ignore = true)
- @Mapping(target = "updated", ignore = true)
- void updateEntityFromDto(RetailCustomerDto dto, @MappingTarget RetailCustomerEntity entity);
-}
\ No newline at end of file
diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/AuthorizationRepository.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/AuthorizationRepository.java
index ed060602..c5b89556 100755
--- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/AuthorizationRepository.java
+++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/AuthorizationRepository.java
@@ -36,7 +36,7 @@ public interface AuthorizationRepository extends JpaRepository findAllByRetailCustomerId(UUID retailCustomerId);
+ List findAllByRetailCustomerId(Long retailCustomerId);
@Query("SELECT a.id FROM AuthorizationEntity a WHERE a.applicationInformation.id = :applicationInformationId")
List findAllIdsByApplicationInformationId(@Param("applicationInformationId") UUID applicationInformationId);
@@ -44,10 +44,10 @@ public interface AuthorizationRepository extends JpaRepository findByState(String state);
@Query("SELECT a FROM AuthorizationEntity a WHERE a.scope = :scope AND a.retailCustomer.id = :retailCustomerId")
- Optional findByScope(@Param("scope") String scope, @Param("retailCustomerId") UUID retailCustomerId);
+ Optional findByScope(@Param("scope") String scope, @Param("retailCustomerId") Long retailCustomerId);
@Query("SELECT a.id FROM AuthorizationEntity a WHERE a.retailCustomer.id = :retailCustomerId")
- List findAllIds(@Param("retailCustomerId") UUID retailCustomerId);
+ List findAllIds(@Param("retailCustomerId") Long retailCustomerId);
// findById is already provided by JpaRepository
// Optional findById(UUID id) is inherited
@@ -77,6 +77,6 @@ public interface AuthorizationRepository extends JpaRepository findExpiredAuthorizations(@Param("currentTime") Long currentTime);
@Query("SELECT a FROM AuthorizationEntity a WHERE a.retailCustomer.id = :customerId AND a.applicationInformation.id = :applicationId AND (a.expiresIn IS NULL OR a.expiresIn > :currentTime)")
- List findActiveByCustomerAndApplication(@Param("customerId") UUID customerId, @Param("applicationId") UUID applicationId, @Param("currentTime") Long currentTime);
+ List findActiveByCustomerAndApplication(@Param("customerId") Long customerId, @Param("applicationId") UUID applicationId, @Param("currentTime") Long currentTime);
}
diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/ElectricPowerQualitySummaryRepository.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/ElectricPowerQualitySummaryRepository.java
index 223cdbb9..9af67fec 100755
--- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/ElectricPowerQualitySummaryRepository.java
+++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/ElectricPowerQualitySummaryRepository.java
@@ -59,9 +59,9 @@ public interface ElectricPowerQualitySummaryRepository extends JpaRepository findAllIdsByUsagePointId(@Param("usagePointId") UUID usagePointId);
@Query("SELECT e.id FROM UsagePointEntity u, ElectricPowerQualitySummaryEntity e WHERE u.retailCustomer.id = :o1Id AND e.usagePoint.id = :o2Id")
- List findAllIdsByXpath2(@Param("o1Id") UUID o1Id, @Param("o2Id") UUID o2Id);
+ List findAllIdsByXpath2(@Param("o1Id") Long o1Id, @Param("o2Id") UUID o2Id);
@Query("SELECT e.id FROM UsagePointEntity u, ElectricPowerQualitySummaryEntity e WHERE u.retailCustomer.id = :o1Id AND e.usagePoint.id = :o2Id AND e.id = :o3Id")
- Optional findIdByXpath(@Param("o1Id") UUID o1Id, @Param("o2Id") UUID o2Id, @Param("o3Id") UUID o3Id);
+ Optional findIdByXpath(@Param("o1Id") Long o1Id, @Param("o2Id") UUID o2Id, @Param("o3Id") UUID o3Id);
}
diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/RetailCustomerRepository.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/RetailCustomerRepository.java
index ea02e0c0..ba32cff7 100644
--- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/RetailCustomerRepository.java
+++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/RetailCustomerRepository.java
@@ -27,32 +27,30 @@
import java.util.List;
import java.util.Optional;
-import java.util.UUID;
/**
* Modern Spring Data JPA repository for RetailCustomer entities.
+ * RetailCustomer is an application-specific correlation table (not part of ESPI standard).
* Replaces the legacy RetailCustomerRepositoryImpl with modern Spring Data patterns.
*/
@Repository
-public interface RetailCustomerRepository extends JpaRepository {
+public interface RetailCustomerRepository extends JpaRepository {
// JpaRepository provides: save(), findById(), findAll(), deleteById(), etc.
/**
- * Find retail customer by username.
+ * Find retail customer by username (indexed).
*/
Optional findByUsername(String username);
/**
* Find retail customers by role.
*/
- @Query("SELECT rc FROM RetailCustomerEntity rc WHERE rc.role = :role")
- List findByRole(@Param("role") String role);
+ List findByRole(String role);
/**
* Find enabled retail customers.
*/
- @Query("SELECT rc FROM RetailCustomerEntity rc WHERE rc.enabled = true")
List findByEnabledTrue();
/**
@@ -63,8 +61,7 @@ public interface RetailCustomerRepository extends JpaRepository findByFirstNameAndLastName(@Param("firstName") String firstName, @Param("lastName") String lastName);
+ List findByFirstNameAndLastName(String firstName, String lastName);
/**
* Check if username exists.
@@ -77,10 +74,10 @@ public interface RetailCustomerRepository extends JpaRepository findAllIds();
+ List findAllIds();
/**
* Find retail customers created after timestamp.
@@ -97,6 +94,5 @@ public interface RetailCustomerRepository extends JpaRepository findLockedAccounts();
+ List findByAccountLockedTrue();
}
\ No newline at end of file
diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/SubscriptionRepository.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/SubscriptionRepository.java
index d57de7de..e5d5448f 100755
--- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/SubscriptionRepository.java
+++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/SubscriptionRepository.java
@@ -55,7 +55,7 @@ public interface SubscriptionRepository extends JpaRepository findAllIds();
@Query("SELECT s FROM SubscriptionEntity s WHERE s.retailCustomer.id = :retailCustomerId")
- List findByRetailCustomerId(@Param("retailCustomerId") UUID retailCustomerId);
+ List findByRetailCustomerId(@Param("retailCustomerId") Long retailCustomerId);
@Query("SELECT s FROM SubscriptionEntity s WHERE s.applicationInformation.id = :applicationInformationId")
List findByApplicationInformationId(@Param("applicationInformationId") UUID applicationInformationId);
diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/UsagePointRepository.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/UsagePointRepository.java
index c8dd51d5..043c7a3a 100755
--- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/UsagePointRepository.java
+++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/UsagePointRepository.java
@@ -43,7 +43,7 @@ public interface UsagePointRepository extends JpaRepository findAllByRetailCustomerId(@Param("retailCustomerId") UUID retailCustomerId);
+ List findAllByRetailCustomerId(@Param("retailCustomerId") Long retailCustomerId);
/**
* Find usage point by resource URI.
@@ -67,7 +67,7 @@ public interface UsagePointRepository extends JpaRepository findAllIdsByRetailCustomerId(@Param("retailCustomerId") UUID retailCustomerId);
+ List findAllIdsByRetailCustomerId(@Param("retailCustomerId") Long retailCustomerId);
/**
* Find all usage point IDs.
diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/UsageSummaryRepository.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/UsageSummaryRepository.java
index 0280b1bc..c87c8966 100644
--- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/UsageSummaryRepository.java
+++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/UsageSummaryRepository.java
@@ -59,9 +59,9 @@ public interface UsageSummaryRepository extends JpaRepository findAllIdsByUsagePointId(@Param("usagePointId") UUID usagePointId);
@Query("SELECT e.id FROM UsagePointEntity u, UsageSummaryEntity e WHERE u.retailCustomer.id = :o1Id AND e.usagePoint.id = :o2Id")
- List findAllIdsByXpath2(@Param("o1Id") UUID o1Id, @Param("o2Id") UUID o2Id);
+ List findAllIdsByXpath2(@Param("o1Id") Long o1Id, @Param("o2Id") UUID o2Id);
@Query("SELECT e.id FROM UsagePointEntity u, UsageSummaryEntity e WHERE u.retailCustomer.id = :o1Id AND e.usagePoint.id = :o2Id AND e.id = :o3Id")
- Optional findIdByXpath(@Param("o1Id") UUID o1Id, @Param("o2Id") UUID o2Id, @Param("o3Id") UUID o3Id);
+ Optional findIdByXpath(@Param("o1Id") Long o1Id, @Param("o2Id") UUID o2Id, @Param("o3Id") UUID o3Id);
}
diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/AuthorizationService.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/AuthorizationService.java
index 23936c07..38408e7b 100755
--- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/AuthorizationService.java
+++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/AuthorizationService.java
@@ -28,7 +28,7 @@
public interface AuthorizationService {
// residue from random stories
- List findAllByRetailCustomerId(UUID retailCustomerId);
+ List findAllByRetailCustomerId(Long retailCustomerId);
/**
* @param applicationInformationId
@@ -43,7 +43,7 @@ AuthorizationEntity createAuthorizationEntity(SubscriptionEntity subscription,
AuthorizationEntity findByState(String state);
- AuthorizationEntity findByScope(String scope, UUID retailCustomerId);
+ AuthorizationEntity findByScope(String scope, Long retailCustomerId);
AuthorizationEntity findByAccessToken(String accessToken);
@@ -68,7 +68,7 @@ AuthorizationEntity createAuthorizationEntity(SubscriptionEntity subscription,
// import-exportResource services
AuthorizationEntity importResource(InputStream stream);
- AuthorizationEntity findById(UUID retailCustomerId, UUID authorizationId);
+ AuthorizationEntity findById(Long retailCustomerId, UUID authorizationId);
AuthorizationEntity findByUUID(UUID uuid);
diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/RetailCustomerService.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/RetailCustomerService.java
index ff41884c..eabcf0bd 100755
--- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/RetailCustomerService.java
+++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/RetailCustomerService.java
@@ -20,31 +20,20 @@
package org.greenbuttonalliance.espi.common.service;
import org.greenbuttonalliance.espi.common.domain.usage.RetailCustomerEntity;
-import org.greenbuttonalliance.espi.common.domain.usage.SubscriptionEntity;
-import java.io.InputStream;
import java.util.List;
-import java.util.UUID;
+/**
+ * Service interface for RetailCustomer operations.
+ * RetailCustomer is an application-specific correlation table (not part of ESPI standard).
+ */
public interface RetailCustomerService {
List findAll();
- RetailCustomerEntity findByHashedId(UUID retailCustomerId);
+ RetailCustomerEntity findById(Long retailCustomerId);
RetailCustomerEntity save(RetailCustomerEntity customer);
- RetailCustomerEntity findById(UUID retailCustomerId);
-
- RetailCustomerEntity findById(String retailCustomerId);
-
- void add(RetailCustomerEntity retailCustomer);
-
- void delete(RetailCustomerEntity retailCustomer);
-
- RetailCustomerEntity importResource(InputStream stream);
-
- SubscriptionEntity associateByUUID(UUID retailCustomerId, UUID uuId);
-
RetailCustomerEntity findByUsername(String username);
}
diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/SubscriptionService.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/SubscriptionService.java
index 63f9960d..201d5fb2 100755
--- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/SubscriptionService.java
+++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/SubscriptionService.java
@@ -45,6 +45,6 @@ public interface SubscriptionService {
SubscriptionEntity addUsagePoint(SubscriptionEntity subscription,
UsagePointEntity usagePoint);
- UUID findRetailCustomerId(UUID subscriptionId, UUID usagePointId);
+ Long findRetailCustomerId(UUID subscriptionId, UUID usagePointId);
}
diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/UsagePointService.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/UsagePointService.java
index d99f6e74..c09b2d6e 100755
--- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/UsagePointService.java
+++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/UsagePointService.java
@@ -45,19 +45,19 @@ public interface UsagePointService {
void deleteByHashedId(String usagePointHashedId);
- List findAllIdsForRetailCustomer(UUID id);
+ List findAllIdsForRetailCustomer(Long id);
String feedFor(List usagePoints) throws JAXBException;
String entryFor(UsagePointEntity usagePoint);
- List findAllByRetailCustomer(UUID retailCustomerId);
+ List findAllByRetailCustomer(Long retailCustomerId);
UsagePointEntity save(UsagePointEntity usagePoint);
UsagePointEntity findById(UUID usagePointId);
- UsagePointEntity findById(UUID retailCustomerId, UUID usagePointId);
+ UsagePointEntity findById(Long retailCustomerId, UUID usagePointId);
// Legacy EntryType methods removed - incompatible with Spring Boot 3.5
diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/impl/AuthorizationServiceImpl.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/impl/AuthorizationServiceImpl.java
index 283004c4..f426b335 100755
--- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/impl/AuthorizationServiceImpl.java
+++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/impl/AuthorizationServiceImpl.java
@@ -50,7 +50,7 @@ public class AuthorizationServiceImpl implements AuthorizationService {
private final AuthorizationMapper authorizationMapper;
@Override
- public List findAllByRetailCustomerId(UUID retailCustomerId) {
+ public List findAllByRetailCustomerId(Long retailCustomerId) {
return authorizationRepository
.findAllByRetailCustomerId(retailCustomerId);
}
@@ -84,7 +84,7 @@ public AuthorizationEntity findByState(String state) {
}
@Override
- public AuthorizationEntity findByScope(String scope, UUID retailCustomerId) {
+ public AuthorizationEntity findByScope(String scope, Long retailCustomerId) {
return authorizationRepository.findByScope(scope, retailCustomerId).orElse(null);
}
@@ -173,7 +173,7 @@ public AuthorizationEntity importResource(InputStream stream) {
}
@Override
- public AuthorizationEntity findById(UUID retailCustomerId, UUID authorizationId) {
+ public AuthorizationEntity findById(Long retailCustomerId, UUID authorizationId) {
return this.authorizationRepository.findById(authorizationId).orElse(null);
}
diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/impl/RetailCustomerServiceImpl.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/impl/RetailCustomerServiceImpl.java
index f95cf783..6f3986eb 100755
--- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/impl/RetailCustomerServiceImpl.java
+++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/impl/RetailCustomerServiceImpl.java
@@ -22,20 +22,18 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.greenbuttonalliance.espi.common.domain.usage.RetailCustomerEntity;
-import org.greenbuttonalliance.espi.common.domain.usage.SubscriptionEntity;
-import org.greenbuttonalliance.espi.common.dto.usage.RetailCustomerDto;
-import org.greenbuttonalliance.espi.common.mapper.usage.RetailCustomerMapper;
import org.greenbuttonalliance.espi.common.repositories.usage.RetailCustomerRepository;
import org.greenbuttonalliance.espi.common.service.RetailCustomerService;
-import org.greenbuttonalliance.espi.common.service.UsagePointService;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
-import java.io.InputStream;
import java.util.List;
-import java.util.UUID;
+/**
+ * Service implementation for RetailCustomer operations.
+ * RetailCustomer is an application-specific correlation table (not part of ESPI standard).
+ */
@Slf4j
@Service
@Transactional(rollbackFor = { jakarta.xml.bind.JAXBException.class }, noRollbackFor = {
@@ -45,8 +43,6 @@
public class RetailCustomerServiceImpl implements RetailCustomerService {
private final RetailCustomerRepository retailCustomerRepository;
- private final RetailCustomerMapper retailCustomerMapper;
- private final UsagePointService usagePointService;
@Override
public List findAll() {
@@ -54,31 +50,13 @@ public List findAll() {
}
@Override
- public RetailCustomerEntity save(RetailCustomerEntity customer) {
- if (customer.getId() == null) {
- customer.setId(UUID.randomUUID());
- }
- return retailCustomerRepository.save(customer);
- }
-
- @Override
- public RetailCustomerEntity findById(UUID id) {
+ public RetailCustomerEntity findById(Long id) {
return retailCustomerRepository.findById(id).orElse(null);
}
@Override
- public RetailCustomerEntity findById(String retailCustomerId) {
- try {
- UUID id = UUID.fromString(retailCustomerId);
- return retailCustomerRepository.findById(id).orElse(null);
- } catch (IllegalArgumentException e) {
- return null;
- }
- }
-
- @Override
- public RetailCustomerEntity findByHashedId(UUID retailCustomerId) {
- return findById(retailCustomerId);
+ public RetailCustomerEntity save(RetailCustomerEntity customer) {
+ return retailCustomerRepository.save(customer);
}
@Override
@@ -91,52 +69,4 @@ public RetailCustomerEntity findByUsername(String username) {
}
}
- @Override
- public void add(RetailCustomerEntity retailCustomer) {
- retailCustomerRepository.save(retailCustomer);
- log.info("Added retail customer: " + retailCustomer.getId());
- }
-
- @Override
- public void delete(RetailCustomerEntity retailCustomer) {
- retailCustomerRepository.deleteById(retailCustomer.getId());
- log.info("Deleted retail customer: " + retailCustomer.getId());
- }
-
- @Override
- public RetailCustomerEntity importResource(InputStream stream) {
- try {
- // Use JAXB to parse XML stream to DTO
- jakarta.xml.bind.JAXBContext context = jakarta.xml.bind.JAXBContext.newInstance(RetailCustomerDto.class);
- jakarta.xml.bind.Unmarshaller unmarshaller = context.createUnmarshaller();
- RetailCustomerDto dto = (RetailCustomerDto) unmarshaller.unmarshal(stream);
-
- // Convert DTO to Entity using mapper
- RetailCustomerEntity entity = retailCustomerMapper.toEntity(dto);
-
- // Save and return entity
- return retailCustomerRepository.save(entity);
-
- } catch (Exception e) {
- // Security: Log error without exposing sensitive customer data
- log.error("RetailCustomerService.importResource failed: " + e.getMessage());
- return null;
- }
- }
-
- @Override
- public SubscriptionEntity associateByUUID(UUID retailCustomerId, UUID uuid) {
- // TODO: Implement modern association logic using entity classes
- log.info("Associating usage point UUID " + uuid + " with retail customer " + retailCustomerId);
-
- // Use the UsagePointService to handle the association
- RetailCustomerEntity retailCustomer = findById(retailCustomerId);
- if (retailCustomer != null) {
- usagePointService.associateByUUID(retailCustomer, uuid);
- }
-
- // TODO: Return appropriate subscription entity
- return null;
- }
-
}
diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/impl/SubscriptionServiceImpl.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/impl/SubscriptionServiceImpl.java
index a77acc61..88d57000 100755
--- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/impl/SubscriptionServiceImpl.java
+++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/impl/SubscriptionServiceImpl.java
@@ -147,8 +147,8 @@ public SubscriptionEntity addUsagePoint(SubscriptionEntity subscription,
}
@Override
- public UUID findRetailCustomerId(UUID subscriptionId, UUID usagePointId) {
- UUID result = null;
+ public Long findRetailCustomerId(UUID subscriptionId, UUID usagePointId) {
+ Long result = null;
SubscriptionEntity subscription = findById(subscriptionId);
if (subscription != null && subscription.getRetailCustomer() != null) {
result = subscription.getRetailCustomer().getId();
diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/impl/UsagePointServiceImpl.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/impl/UsagePointServiceImpl.java
index 6635c999..231b31f9 100755
--- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/impl/UsagePointServiceImpl.java
+++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/impl/UsagePointServiceImpl.java
@@ -63,7 +63,7 @@ public UsagePointEntity findById(UUID usagePointId) {
}
@Override
- public UsagePointEntity findById(UUID retailCustomerId, UUID usagePointId) {
+ public UsagePointEntity findById(Long retailCustomerId, UUID usagePointId) {
// TODO: Implement scoped query for retailCustomer.usagePoint
return usagePointRepository.findById(usagePointId).orElse(null);
}
@@ -119,7 +119,7 @@ public void deleteByHashedId(String usagePointHashedId) {
}
@Override
- public List findAllIdsForRetailCustomer(UUID id) {
+ public List findAllIdsForRetailCustomer(Long id) {
return usagePointRepository
.findAllIdsByRetailCustomerId(id);
}
@@ -139,7 +139,7 @@ public String entryFor(UsagePointEntity usagePoint) {
}
@Override
- public List findAllByRetailCustomer(UUID retailCustomerId) {
+ public List findAllByRetailCustomer(Long retailCustomerId) {
return usagePointRepository.findAllByRetailCustomerId(retailCustomerId);
}
diff --git a/openespi-common/src/main/resources/db/migration/V1__Create_Base_Tables.sql b/openespi-common/src/main/resources/db/migration/V1__Create_Base_Tables.sql
index 9d1dbac4..11873a1d 100644
--- a/openespi-common/src/main/resources/db/migration/V1__Create_Base_Tables.sql
+++ b/openespi-common/src/main/resources/db/migration/V1__Create_Base_Tables.sql
@@ -137,39 +137,9 @@ CREATE TABLE application_information_scopes
CREATE INDEX idx_app_info_scopes ON application_information_scopes (application_information_id);
--- Retail Customer Table
-CREATE TABLE retail_customers
-(
- id CHAR(36) PRIMARY KEY,
- description VARCHAR(255),
- created TIMESTAMP NOT NULL,
- updated TIMESTAMP NOT NULL,
- published TIMESTAMP,
- up_link_rel VARCHAR(255),
- up_link_href VARCHAR(1024),
- up_link_type VARCHAR(255),
- self_link_rel VARCHAR(255),
- self_link_href VARCHAR(1024),
- self_link_type VARCHAR(255),
-
- -- Retail customer specific fields
- username VARCHAR(255) UNIQUE,
- first_name VARCHAR(255),
- last_name VARCHAR(255),
- password VARCHAR(255),
- enabled BOOLEAN DEFAULT TRUE,
- role VARCHAR(50) DEFAULT 'ROLE_USER',
- email VARCHAR(100),
- phone VARCHAR(20),
- account_created BIGINT,
- last_login BIGINT,
- account_locked BOOLEAN DEFAULT FALSE,
- failed_login_attempts INTEGER DEFAULT 0
-);
-
-CREATE INDEX idx_retail_customer_username ON retail_customers (username);
-CREATE INDEX idx_retail_customer_created ON retail_customers (created);
-CREATE INDEX idx_retail_customer_updated ON retail_customers (updated);
+-- Retail Customer Table - Moved to vendor-specific V2 migration files
+-- RetailCustomer is an application-specific correlation table (not part of ESPI standard)
+-- Table creation moved to V2 vendor files due to auto-increment syntax differences
-- Service Delivery Point Table - Moved to vendor-specific V2 migration files
-- ServiceDeliveryPoint extends Object (not IdentifiedObject) per ESPI 4.0 XSD (espi.xsd:1161)
@@ -196,7 +166,7 @@ CREATE TABLE authorizations
published_period_start BIGINT,
published_period_duration BIGINT,
application_information_id CHAR(36),
- retail_customer_id CHAR(36),
+ retail_customer_id BIGINT,
subscription_id CHAR(36),
access_token VARCHAR(1024),
refresh_token VARCHAR(1024),
@@ -220,8 +190,8 @@ CREATE TABLE authorizations
response_type VARCHAR(50),
third_party VARCHAR(255),
- FOREIGN KEY (application_information_id) REFERENCES application_information (id) ON DELETE CASCADE,
- FOREIGN KEY (retail_customer_id) REFERENCES retail_customers (id) ON DELETE CASCADE
+ FOREIGN KEY (application_information_id) REFERENCES application_information (id) ON DELETE CASCADE
+ -- FK constraint for retail_customer_id added in V2 after retail_customers table is created
);
CREATE INDEX idx_authorization_app_id ON authorizations (application_information_id);
@@ -318,10 +288,10 @@ CREATE TABLE subscriptions
-- Foreign key relationships
application_information_id CHAR(36),
authorization_id CHAR(36),
- retail_customer_id CHAR(36),
+ retail_customer_id BIGINT,
- FOREIGN KEY (application_information_id) REFERENCES application_information (id) ON DELETE CASCADE,
- FOREIGN KEY (retail_customer_id) REFERENCES retail_customers (id) ON DELETE CASCADE
+ FOREIGN KEY (application_information_id) REFERENCES application_information (id) ON DELETE CASCADE
+ -- FK constraint for retail_customer_id added in V2 after retail_customers table is created
);
CREATE INDEX idx_subscription_app_id ON subscriptions (application_information_id);
diff --git a/openespi-common/src/main/resources/db/vendor/h2/V2__H2_Specific_Tables.sql b/openespi-common/src/main/resources/db/vendor/h2/V2__H2_Specific_Tables.sql
index a57caec2..e6d334b1 100644
--- a/openespi-common/src/main/resources/db/vendor/h2/V2__H2_Specific_Tables.sql
+++ b/openespi-common/src/main/resources/db/vendor/h2/V2__H2_Specific_Tables.sql
@@ -135,12 +135,12 @@ CREATE TABLE usage_points
rated_power_reading_type_ref VARCHAR(512),
-- Foreign key relationships
- retail_customer_id CHAR(36),
+ retail_customer_id BIGINT,
service_delivery_point_id BIGINT,
local_time_parameters_id CHAR(36),
subscription_id CHAR(36),
- FOREIGN KEY (retail_customer_id) REFERENCES retail_customers (id) ON DELETE CASCADE,
+ -- FK constraint for retail_customer_id added at end of V2 after retail_customers table is created
FOREIGN KEY (service_delivery_point_id) REFERENCES service_delivery_points (id) ON DELETE SET NULL,
FOREIGN KEY (local_time_parameters_id) REFERENCES time_configurations (id) ON DELETE SET NULL
);
@@ -544,3 +544,43 @@ CREATE TABLE line_items
CREATE INDEX idx_line_item_usage_summary ON line_items (usage_summary_id);
CREATE INDEX idx_line_item_date_time ON line_items (date_time);
CREATE INDEX idx_line_item_amount ON line_items (amount);
+
+-- Retail Customer Table (Application-specific correlation table)
+-- RetailCustomer is an application-specific correlation table (not part of ESPI standard)
+-- Used to correlate UsagePoint energy data to individual users
+CREATE TABLE retail_customers
+(
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
+
+ -- Application correlation fields
+ username VARCHAR(255) UNIQUE,
+ first_name VARCHAR(255),
+ last_name VARCHAR(255),
+ password VARCHAR(255),
+ enabled BOOLEAN DEFAULT TRUE,
+ role VARCHAR(50) DEFAULT 'ROLE_USER',
+ email VARCHAR(100),
+ phone VARCHAR(20),
+ account_created BIGINT,
+ last_login BIGINT,
+ account_locked BOOLEAN DEFAULT FALSE,
+ failed_login_attempts INTEGER DEFAULT 0
+);
+
+-- Create index for authentication hot path
+CREATE INDEX idx_retail_customer_username ON retail_customers (username);
+
+-- Add foreign key constraint for authorizations.retail_customer_id
+-- (Column type changed to BIGINT in V1, FK constraint added here after retail_customers table exists)
+ALTER TABLE authorizations ADD CONSTRAINT fk_authorization_retail_customer
+ FOREIGN KEY (retail_customer_id) REFERENCES retail_customers (id) ON DELETE CASCADE;
+
+-- Add foreign key constraint for subscriptions.retail_customer_id
+-- (Column type changed to BIGINT in V1, FK constraint added here after retail_customers table exists)
+ALTER TABLE subscriptions ADD CONSTRAINT fk_subscription_retail_customer
+ FOREIGN KEY (retail_customer_id) REFERENCES retail_customers (id) ON DELETE CASCADE;
+
+-- Add foreign key constraint for usage_points.retail_customer_id
+-- (Column type changed to BIGINT, FK constraint added here after retail_customers table is created)
+ALTER TABLE usage_points ADD CONSTRAINT fk_usage_point_retail_customer
+ FOREIGN KEY (retail_customer_id) REFERENCES retail_customers (id) ON DELETE CASCADE;
diff --git a/openespi-common/src/main/resources/db/vendor/mysql/V2__MySQL_Specific_Tables.sql b/openespi-common/src/main/resources/db/vendor/mysql/V2__MySQL_Specific_Tables.sql
index 2ec9948e..39d8383d 100644
--- a/openespi-common/src/main/resources/db/vendor/mysql/V2__MySQL_Specific_Tables.sql
+++ b/openespi-common/src/main/resources/db/vendor/mysql/V2__MySQL_Specific_Tables.sql
@@ -132,12 +132,12 @@ CREATE TABLE usage_points
rated_power_reading_type_ref VARCHAR(512),
-- Foreign key relationships
- retail_customer_id CHAR(36),
+ retail_customer_id BIGINT,
service_delivery_point_id BIGINT,
local_time_parameters_id CHAR(36),
subscription_id CHAR(36),
- FOREIGN KEY (retail_customer_id) REFERENCES retail_customers (id) ON DELETE CASCADE,
+ -- FK constraint for retail_customer_id added at end of V2 after retail_customers table is created
FOREIGN KEY (service_delivery_point_id) REFERENCES service_delivery_points (id) ON DELETE SET NULL,
FOREIGN KEY (local_time_parameters_id) REFERENCES time_configurations (id) ON DELETE SET NULL,
@@ -542,3 +542,43 @@ CREATE TABLE line_items
INDEX idx_line_item_date_time (date_time),
INDEX idx_line_item_amount (amount)
);
+
+-- Retail Customer Table (Application-specific correlation table)
+-- RetailCustomer is an application-specific correlation table (not part of ESPI standard)
+-- Used to correlate UsagePoint energy data to individual users
+CREATE TABLE retail_customers
+(
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
+
+ -- Application correlation fields
+ username VARCHAR(255) UNIQUE,
+ first_name VARCHAR(255),
+ last_name VARCHAR(255),
+ password VARCHAR(255),
+ enabled BOOLEAN DEFAULT TRUE,
+ role VARCHAR(50) DEFAULT 'ROLE_USER',
+ email VARCHAR(100),
+ phone VARCHAR(20),
+ account_created BIGINT,
+ last_login BIGINT,
+ account_locked BOOLEAN DEFAULT FALSE,
+ failed_login_attempts INTEGER DEFAULT 0
+);
+
+-- Create index for authentication hot path
+CREATE INDEX idx_retail_customer_username ON retail_customers (username);
+
+-- Add foreign key constraint for authorizations.retail_customer_id
+-- (Column type changed to BIGINT in V1, FK constraint added here after retail_customers table exists)
+ALTER TABLE authorizations ADD CONSTRAINT fk_authorization_retail_customer
+ FOREIGN KEY (retail_customer_id) REFERENCES retail_customers (id) ON DELETE CASCADE;
+
+-- Add foreign key constraint for subscriptions.retail_customer_id
+-- (Column type changed to BIGINT in V1, FK constraint added here after retail_customers table exists)
+ALTER TABLE subscriptions ADD CONSTRAINT fk_subscription_retail_customer
+ FOREIGN KEY (retail_customer_id) REFERENCES retail_customers (id) ON DELETE CASCADE;
+
+-- Add foreign key constraint for usage_points.retail_customer_id
+-- (Column type changed to BIGINT, FK constraint added here after retail_customers table is created)
+ALTER TABLE usage_points ADD CONSTRAINT fk_usage_point_retail_customer
+ FOREIGN KEY (retail_customer_id) REFERENCES retail_customers (id) ON DELETE CASCADE;
diff --git a/openespi-common/src/main/resources/db/vendor/postgres/V2__PostgreSQL_Specific_Tables.sql b/openespi-common/src/main/resources/db/vendor/postgres/V2__PostgreSQL_Specific_Tables.sql
index 3914b5a5..8649c391 100644
--- a/openespi-common/src/main/resources/db/vendor/postgres/V2__PostgreSQL_Specific_Tables.sql
+++ b/openespi-common/src/main/resources/db/vendor/postgres/V2__PostgreSQL_Specific_Tables.sql
@@ -133,12 +133,12 @@ CREATE TABLE usage_points
rated_power_reading_type_ref VARCHAR(512),
-- Foreign key relationships
- retail_customer_id CHAR(36),
+ retail_customer_id BIGINT,
service_delivery_point_id BIGINT,
local_time_parameters_id CHAR(36),
subscription_id CHAR(36),
- FOREIGN KEY (retail_customer_id) REFERENCES retail_customers (id) ON DELETE CASCADE,
+ -- FK constraint for retail_customer_id added at end of V2 after retail_customers table is created
FOREIGN KEY (service_delivery_point_id) REFERENCES service_delivery_points (id) ON DELETE SET NULL,
FOREIGN KEY (local_time_parameters_id) REFERENCES time_configurations (id) ON DELETE SET NULL
);
@@ -531,3 +531,43 @@ CREATE TABLE line_items
CREATE INDEX idx_line_item_usage_summary ON line_items (usage_summary_id);
CREATE INDEX idx_line_item_date_time ON line_items (date_time);
CREATE INDEX idx_line_item_amount ON line_items (amount);
+
+-- Retail Customer Table (Application-specific correlation table)
+-- RetailCustomer is an application-specific correlation table (not part of ESPI standard)
+-- Used to correlate UsagePoint energy data to individual users
+CREATE TABLE retail_customers
+(
+ id BIGSERIAL PRIMARY KEY,
+
+ -- Application correlation fields
+ username VARCHAR(255) UNIQUE,
+ first_name VARCHAR(255),
+ last_name VARCHAR(255),
+ password VARCHAR(255),
+ enabled BOOLEAN DEFAULT TRUE,
+ role VARCHAR(50) DEFAULT 'ROLE_USER',
+ email VARCHAR(100),
+ phone VARCHAR(20),
+ account_created BIGINT,
+ last_login BIGINT,
+ account_locked BOOLEAN DEFAULT FALSE,
+ failed_login_attempts INTEGER DEFAULT 0
+);
+
+-- Create index for authentication hot path
+CREATE INDEX idx_retail_customer_username ON retail_customers (username);
+
+-- Add foreign key constraint for authorizations.retail_customer_id
+-- (Column type changed to BIGINT in V1, FK constraint added here after retail_customers table exists)
+ALTER TABLE authorizations ADD CONSTRAINT fk_authorization_retail_customer
+ FOREIGN KEY (retail_customer_id) REFERENCES retail_customers (id) ON DELETE CASCADE;
+
+-- Add foreign key constraint for subscriptions.retail_customer_id
+-- (Column type changed to BIGINT in V1, FK constraint added here after retail_customers table exists)
+ALTER TABLE subscriptions ADD CONSTRAINT fk_subscription_retail_customer
+ FOREIGN KEY (retail_customer_id) REFERENCES retail_customers (id) ON DELETE CASCADE;
+
+-- Add foreign key constraint for usage_points.retail_customer_id
+-- (Column type changed to BIGINT, FK constraint added here after retail_customers table is created)
+ALTER TABLE usage_points ADD CONSTRAINT fk_usage_point_retail_customer
+ FOREIGN KEY (retail_customer_id) REFERENCES retail_customers (id) ON DELETE CASCADE;
diff --git a/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/AuthorizationRepositoryTest.java b/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/AuthorizationRepositoryTest.java
index 7a5ea7d3..b1a050a0 100644
--- a/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/AuthorizationRepositoryTest.java
+++ b/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/AuthorizationRepositoryTest.java
@@ -379,7 +379,7 @@ void shouldFindExpiredAuthorizations() {
@DisplayName("Should handle empty results gracefully")
void shouldHandleEmptyResultsGracefully() {
// Act & Assert
- assertThat(authorizationRepository.findAllByRetailCustomerId(UUID.randomUUID())).isEmpty();
+ assertThat(authorizationRepository.findAllByRetailCustomerId(999999L)).isEmpty();
assertThat(authorizationRepository.findByState("nonexistent-state")).isEmpty();
assertThat(authorizationRepository.findByAccessToken("nonexistent-token")).isEmpty();
assertThat(authorizationRepository.findByRefreshToken("nonexistent-refresh-token")).isEmpty();
diff --git a/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/RetailCustomerRepositoryTest.java b/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/RetailCustomerRepositoryTest.java
index dd3b1ed7..20a82e41 100644
--- a/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/RetailCustomerRepositoryTest.java
+++ b/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/RetailCustomerRepositoryTest.java
@@ -32,15 +32,16 @@
import java.util.List;
import java.util.Optional;
import java.util.Set;
-import java.util.UUID;
import static org.assertj.core.api.Assertions.*;
/**
* Comprehensive test suite for RetailCustomerRepository.
- *
- * Tests all CRUD operations, 11 custom query methods, relationships,
+ *
+ * Tests all CRUD operations, custom query methods, relationships,
* and validation constraints for RetailCustomer entities.
+ *
+ * Note: RetailCustomer is an application-specific correlation table (not part of ESPI standard).
*/
@DisplayName("RetailCustomer Repository Tests")
class RetailCustomerRepositoryTest extends BaseRepositoryTest {
@@ -57,8 +58,9 @@ class CrudOperationsTest {
void shouldSaveAndRetrieveRetailCustomerSuccessfully() {
// Arrange
RetailCustomerEntity retailCustomer = TestDataBuilders.createValidRetailCustomer();
- retailCustomer.setDescription("Test Retail Customer for CRUD");
retailCustomer.setUsername("testuser@example.com");
+ retailCustomer.setFirstName("Test");
+ retailCustomer.setLastName("User");
// Act
RetailCustomerEntity saved = retailCustomerRepository.save(retailCustomer);
@@ -69,8 +71,9 @@ void shouldSaveAndRetrieveRetailCustomerSuccessfully() {
assertThat(saved).isNotNull();
assertThat(saved.getId()).isNotNull();
assertThat(retrieved).isPresent();
- assertThat(retrieved.get().getDescription()).isEqualTo("Test Retail Customer for CRUD");
assertThat(retrieved.get().getUsername()).isEqualTo("testuser@example.com");
+ assertThat(retrieved.get().getFirstName()).isEqualTo("Test");
+ assertThat(retrieved.get().getLastName()).isEqualTo("User");
assertThat(retrieved.get().getEnabled()).isTrue();
}
@@ -99,7 +102,7 @@ void shouldDeleteRetailCustomerSuccessfully() {
RetailCustomerEntity retailCustomer = TestDataBuilders.createValidRetailCustomer();
retailCustomer.setUsername("delete@example.com");
RetailCustomerEntity saved = retailCustomerRepository.save(retailCustomer);
- UUID retailCustomerId = saved.getId();
+ Long retailCustomerId = saved.getId();
flushAndClear();
// Act
@@ -121,7 +124,7 @@ void shouldCheckIfRetailCustomerExists() {
// Act & Assert
assertThat(retailCustomerRepository.existsById(saved.getId())).isTrue();
- assertThat(retailCustomerRepository.existsById(UUID.randomUUID())).isFalse();
+ assertThat(retailCustomerRepository.existsById(999999L)).isFalse();
}
@Test
@@ -324,7 +327,7 @@ void shouldFindAllRetailCustomerIds() {
flushAndClear();
// Act
- List allIds = retailCustomerRepository.findAllIds();
+ List allIds = retailCustomerRepository.findAllIds();
// Assert
assertThat(allIds).hasSizeGreaterThanOrEqualTo(3);
@@ -413,7 +416,7 @@ void shouldFindLockedAccounts() {
flushAndClear();
// Act
- List lockedAccounts = retailCustomerRepository.findLockedAccounts();
+ List lockedAccounts = retailCustomerRepository.findByAccountLockedTrue();
// Assert
assertThat(lockedAccounts).hasSize(2);
@@ -561,54 +564,45 @@ void shouldValidateRoleField() {
}
@Nested
- @DisplayName("Base Class Functionality")
- class BaseClassTest {
+ @DisplayName("Entity Functionality")
+ class EntityFunctionalityTest {
@Test
- @DisplayName("Should inherit IdentifiedObject functionality")
- void shouldInheritIdentifiedObjectFunctionality() {
- // Arrange & Act
- RetailCustomerEntity retailCustomer = TestDataBuilders.createValidRetailCustomer();
-
- // Assert
- assertThat(retailCustomer.getId()).isNotNull();
- assertThat(retailCustomer.getId()).isInstanceOf(UUID.class);
- // RetailCustomerEntity extends IdentifiedObject and inherits UUID functionality
- }
-
- @Test
- @DisplayName("Should set timestamps on persist")
- void shouldSetTimestampsOnPersist() {
+ @DisplayName("Should auto-generate ID on persist")
+ void shouldAutoGenerateIdOnPersist() {
// Arrange
RetailCustomerEntity retailCustomer = TestDataBuilders.createValidRetailCustomer();
- retailCustomer.setUsername("timestamp@example.com");
+ retailCustomer.setUsername("autoid@example.com");
+
+ // Verify ID is null before save
+ assertThat(retailCustomer.getId()).isNull();
// Act
RetailCustomerEntity saved = retailCustomerRepository.save(retailCustomer);
flushAndClear();
- // Assert
- assertThat(saved.getCreated()).isNotNull();
- assertThat(saved.getUpdated()).isNotNull();
+ // Assert - ID should be auto-generated by database
+ assertThat(saved.getId()).isNotNull();
+ assertThat(saved.getId()).isInstanceOf(Long.class);
+ assertThat(saved.getId()).isPositive();
}
@Test
- @DisplayName("Should test equals and hashCode")
- void shouldTestEqualsAndHashCode() {
+ @DisplayName("Should test equals and hashCode with Long ID")
+ void shouldTestEqualsAndHashCodeWithLongId() {
// Arrange
- UUID sharedId = UUID.randomUUID();
-
RetailCustomerEntity customer1 = TestDataBuilders.createValidRetailCustomer();
- customer1.setId(sharedId);
customer1.setUsername("customer1@example.com");
-
+ RetailCustomerEntity savedCustomer1 = retailCustomerRepository.save(customer1);
+ flushAndClear();
+
RetailCustomerEntity customer2 = TestDataBuilders.createValidRetailCustomer();
- customer2.setId(sharedId);
+ customer2.setId(savedCustomer1.getId());
customer2.setUsername("customer2@example.com");
- // Act & Assert
- assertThat(customer1).isEqualTo(customer2);
- assertThat(customer1.hashCode()).isEqualTo(customer2.hashCode());
+ // Act & Assert - entities with same ID should be equal
+ assertThat(customer2).isEqualTo(savedCustomer1);
+ assertThat(customer2.hashCode()).isEqualTo(savedCustomer1.hashCode());
}
@Test
@@ -617,14 +611,16 @@ void shouldGenerateMeaningfulToStringRepresentation() {
// Arrange
RetailCustomerEntity retailCustomer = TestDataBuilders.createValidRetailCustomer();
retailCustomer.setUsername("tostring@example.com");
+ RetailCustomerEntity saved = retailCustomerRepository.save(retailCustomer);
+ flushAndClear();
// Act
- String toString = retailCustomer.toString();
+ String toString = saved.toString();
// Assert
assertThat(toString).isNotNull();
assertThat(toString).contains("RetailCustomerEntity");
- assertThat(toString).contains(retailCustomer.getId().toString());
+ assertThat(toString).contains(saved.getId().toString());
}
}
}
\ No newline at end of file
diff --git a/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/SubscriptionRepositoryTest.java b/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/SubscriptionRepositoryTest.java
index 76933423..f4122e1b 100644
--- a/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/SubscriptionRepositoryTest.java
+++ b/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/SubscriptionRepositoryTest.java
@@ -551,7 +551,7 @@ void shouldHandleEmptyResultsGracefully() {
// Act & Assert
assertThat(subscriptionRepository.findByHashedId("nonexistent-hash")).isEmpty();
assertThat(subscriptionRepository.findByAuthorizationId(UUID.randomUUID())).isEmpty();
- assertThat(subscriptionRepository.findByRetailCustomerId(UUID.randomUUID())).isEmpty();
+ assertThat(subscriptionRepository.findByRetailCustomerId(999999L)).isEmpty();
assertThat(subscriptionRepository.findByApplicationInformationId(UUID.randomUUID())).isEmpty();
assertThat(subscriptionRepository.findByUsagePointId(UUID.randomUUID())).isEmpty();
}
diff --git a/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/UsagePointRepositoryTest.java b/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/UsagePointRepositoryTest.java
index 7daade4e..524869b2 100644
--- a/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/UsagePointRepositoryTest.java
+++ b/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/UsagePointRepositoryTest.java
@@ -370,10 +370,10 @@ void shouldDeleteUsagePointByUuid() {
@DisplayName("Should handle empty results gracefully")
void shouldHandleEmptyResultsGracefully() {
// Act & Assert
- assertThat(usagePointRepository.findAllByRetailCustomerId(UUID.randomUUID())).isEmpty();
+ assertThat(usagePointRepository.findAllByRetailCustomerId(999999L)).isEmpty();
assertThat(usagePointRepository.findByResourceUri("nonexistent-uri")).isEmpty();
assertThat(usagePointRepository.findByRelatedHref("nonexistent-href")).isEmpty();
- assertThat(usagePointRepository.findAllIdsByRetailCustomerId(UUID.randomUUID())).isEmpty();
+ assertThat(usagePointRepository.findAllIdsByRetailCustomerId(999999L)).isEmpty();
}
}
diff --git a/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/UsageSummaryRepositoryTest.java b/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/UsageSummaryRepositoryTest.java
index 18c375eb..552d330c 100644
--- a/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/UsageSummaryRepositoryTest.java
+++ b/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/UsageSummaryRepositoryTest.java
@@ -310,7 +310,7 @@ void shouldFindAllIdsByXpath2() {
UsageSummaryEntity summary = createCompleteTestSetup();
UsageSummaryEntity saved = persistAndFlush(summary);
- UUID retailCustomerId = summary.getUsagePoint().getRetailCustomer().getId();
+ Long retailCustomerId = summary.getUsagePoint().getRetailCustomer().getId();
UUID usagePointId = summary.getUsagePoint().getId();
// Act
@@ -327,7 +327,7 @@ void shouldFindIdByXpath() {
UsageSummaryEntity summary = createCompleteTestSetup();
UsageSummaryEntity saved = persistAndFlush(summary);
- UUID retailCustomerId = summary.getUsagePoint().getRetailCustomer().getId();
+ Long retailCustomerId = summary.getUsagePoint().getRetailCustomer().getId();
UUID usagePointId = summary.getUsagePoint().getId();
UUID summaryId = saved.getId();
diff --git a/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/test/TestDataBuilders.java b/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/test/TestDataBuilders.java
index 5a813793..c499ec23 100644
--- a/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/test/TestDataBuilders.java
+++ b/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/test/TestDataBuilders.java
@@ -162,11 +162,11 @@ public static ReadingTypeEntity createValidReadingType() {
/**
* Creates a valid RetailCustomerEntity for testing.
+ * Note: RetailCustomer is an application-specific correlation table (not part of ESPI standard).
*/
public static RetailCustomerEntity createValidRetailCustomer() {
RetailCustomerEntity retailCustomer = new RetailCustomerEntity();
- retailCustomer.setDescription(faker.lorem().sentence(4, 8));
-
+
// Ensure enabled is set properly (@NotNull constraint)
retailCustomer.setEnabled(true);
diff --git a/openespi-datacustodian/src/main/java/org/greenbuttonalliance/espi/datacustodian/web/custodian/AssociateUsagePointController.java b/openespi-datacustodian/src/main/java/org/greenbuttonalliance/espi/datacustodian/web/custodian/AssociateUsagePointController.java
index 244d134e..54e2628c 100644
--- a/openespi-datacustodian/src/main/java/org/greenbuttonalliance/espi/datacustodian/web/custodian/AssociateUsagePointController.java
+++ b/openespi-datacustodian/src/main/java/org/greenbuttonalliance/espi/datacustodian/web/custodian/AssociateUsagePointController.java
@@ -62,7 +62,7 @@ protected void initBinder(WebDataBinder binder) {
}
@GetMapping("/custodian/retailcustomers/{retailCustomerId}/usagepoints/form")
- public String form(@PathVariable UUID retailCustomerId, ModelMap model) {
+ public String form(@PathVariable Long retailCustomerId, ModelMap model) {
model.put("usagePointForm", new UsagePointEntityForm());
model.put("retailCustomerId", retailCustomerId);
@@ -71,20 +71,21 @@ public String form(@PathVariable UUID retailCustomerId, ModelMap model) {
@PostMapping("/custodian/retailcustomers/{retailCustomerId}/usagepoints/create")
public String create(
- @PathVariable UUID retailCustomerId,
+ @PathVariable Long retailCustomerId,
@ModelAttribute("usagePointForm") @Valid UsagePointEntityForm usagePointForm,
BindingResult result) {
if (result.hasErrors())
return "/custodian/retailcustomers/usagepoints/form";
+ // TODO: Implement usage point association - associateById method needs to be implemented
// retailCustomerService returns legacy SubscriptionEntity, not SubscriptionEntityEntity
- var subscription = retailCustomerService.associateByUUID(
- retailCustomerId, UUID.fromString(usagePointForm.getUUID()));
+ // var subscription = retailCustomerService.associateById(
+ // retailCustomerId, UUID.fromString(usagePointForm.getUUID()));
- if (subscription != null) {
+ // if (subscription != null) {
// TODO: Implement NotificationService
// notificationService.notify(subscription, null, null);
- }
+ // }
return "redirect:/custodian/retailcustomers";
}
diff --git a/openespi-datacustodian/src/main/java/org/greenbuttonalliance/espi/datacustodian/web/custodian/RetailCustomerController.java b/openespi-datacustodian/src/main/java/org/greenbuttonalliance/espi/datacustodian/web/custodian/RetailCustomerController.java
index f885484f..806dbeb1 100644
--- a/openespi-datacustodian/src/main/java/org/greenbuttonalliance/espi/datacustodian/web/custodian/RetailCustomerController.java
+++ b/openespi-datacustodian/src/main/java/org/greenbuttonalliance/espi/datacustodian/web/custodian/RetailCustomerController.java
@@ -34,7 +34,6 @@
import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid;
-import java.util.UUID;
@Controller
@PreAuthorize("hasRole('ROLE_CUSTODIAN')")
@@ -83,7 +82,7 @@ public String create(
}
@GetMapping("/custodian/retailcustomers/{retailCustomerId}/show")
- public String show(@PathVariable UUID retailCustomerId, ModelMap model) {
+ public String show(@PathVariable Long retailCustomerId, ModelMap model) {
RetailCustomerEntity retailCustomer = service.findById(retailCustomerId);
model.put("retailCustomer", retailCustomer);
return "/custodian/retailcustomers/show";
diff --git a/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/repository/MeterReadingRESTRepository.java b/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/repository/MeterReadingRESTRepository.java
index 9a9b9a06..adab575e 100644
--- a/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/repository/MeterReadingRESTRepository.java
+++ b/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/repository/MeterReadingRESTRepository.java
@@ -25,6 +25,6 @@
import java.util.UUID;
public interface MeterReadingRESTRepository {
- MeterReadingEntity findByUUID(UUID retailCustomerId, UUID uuid)
+ MeterReadingEntity findByUUID(Long retailCustomerId, UUID uuid)
throws JAXBException;
}
diff --git a/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/repository/UsagePointRESTRepository.java b/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/repository/UsagePointRESTRepository.java
index 6e7dd54b..59cffaed 100755
--- a/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/repository/UsagePointRESTRepository.java
+++ b/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/repository/UsagePointRESTRepository.java
@@ -28,8 +28,8 @@
// TODO repository convergence with common
//
public interface UsagePointRESTRepository {
- List findAllByRetailCustomerId(UUID id) throws JAXBException;
+ List findAllByRetailCustomerId(Long id) throws JAXBException;
- UsagePointEntity findByHashedId(UUID retailCustomerId, String usagePointHashedId)
+ UsagePointEntity findByHashedId(Long retailCustomerId, String usagePointHashedId)
throws JAXBException;
}
diff --git a/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/repository/impl/MeterReadingRESTRepositoryImpl.java b/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/repository/impl/MeterReadingRESTRepositoryImpl.java
index 7db20705..5f87a934 100644
--- a/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/repository/impl/MeterReadingRESTRepositoryImpl.java
+++ b/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/repository/impl/MeterReadingRESTRepositoryImpl.java
@@ -48,7 +48,7 @@ public UsagePointRESTRepository getUsagePointRESTRepository(
}
@Override
- public MeterReadingEntity findByUUID(UUID retailCustomerId, UUID uuid)
+ public MeterReadingEntity findByUUID(Long retailCustomerId, UUID uuid)
throws JAXBException {
List usagePointList = usagePointRESTRepository
.findAllByRetailCustomerId(retailCustomerId);
diff --git a/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/repository/impl/UsagePointRESTRepositoryImpl.java b/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/repository/impl/UsagePointRESTRepositoryImpl.java
index 58817175..0c1043ff 100755
--- a/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/repository/impl/UsagePointRESTRepositoryImpl.java
+++ b/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/repository/impl/UsagePointRESTRepositoryImpl.java
@@ -100,7 +100,7 @@ public void setAuthorizationService(
}
@Override
- public List findAllByRetailCustomerId(UUID retailCustomerId)
+ public List findAllByRetailCustomerId(Long retailCustomerId)
throws JAXBException {
AuthorizationEntity authorization = findAuthorization(retailCustomerId);
@@ -129,7 +129,7 @@ public List findAllByRetailCustomerId(UUID retailCustomerId)
}
@Override
- public UsagePointEntity findByHashedId(UUID retailCustomerId,
+ public UsagePointEntity findByHashedId(Long retailCustomerId,
String usagePointHashedId) throws JAXBException {
List usagePoints = findAllByRetailCustomerId(retailCustomerId);
@@ -143,7 +143,7 @@ public UsagePointEntity findByHashedId(UUID retailCustomerId,
}
- private AuthorizationEntity findAuthorization(UUID retailCustomerId) {
+ private AuthorizationEntity findAuthorization(Long retailCustomerId) {
List authorizations = authorizationService
.findAllByRetailCustomerId(retailCustomerId);
return authorizations.get(authorizations.size() - 1);
diff --git a/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/service/MeterReadingRESTService.java b/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/service/MeterReadingRESTService.java
index 8723b9d5..647c188d 100644
--- a/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/service/MeterReadingRESTService.java
+++ b/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/service/MeterReadingRESTService.java
@@ -26,6 +26,6 @@
public interface MeterReadingRESTService {
- MeterReadingEntity findByUUID(UUID retailCustomerId, UUID uuid)
+ MeterReadingEntity findByUUID(Long retailCustomerId, UUID uuid)
throws JAXBException;
}
diff --git a/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/service/impl/MeterReadingRESTServiceImpl.java b/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/service/impl/MeterReadingRESTServiceImpl.java
index 283eddf8..076f10e9 100644
--- a/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/service/impl/MeterReadingRESTServiceImpl.java
+++ b/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/service/impl/MeterReadingRESTServiceImpl.java
@@ -34,7 +34,7 @@ public class MeterReadingRESTServiceImpl implements MeterReadingRESTService {
protected MeterReadingRESTRepository repository;
@Override
- public MeterReadingEntity findByUUID(UUID retailCustomerId, UUID uuid)
+ public MeterReadingEntity findByUUID(Long retailCustomerId, UUID uuid)
throws JAXBException {
return repository.findByUUID(retailCustomerId, uuid);
}
diff --git a/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/utils/factories/Factory.java b/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/utils/factories/Factory.java
index 624a19af..d5650263 100644
--- a/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/utils/factories/Factory.java
+++ b/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/utils/factories/Factory.java
@@ -36,7 +36,7 @@ public static UsagePointEntity newUsagePoint() {
usagePoint.setServiceCategory(ServiceCategory.ELECTRICITY);
RetailCustomerEntity retailCustomer = new RetailCustomerEntity();
- retailCustomer.setId(UUID.randomUUID());
+ retailCustomer.setId(1000000L);
usagePoint.setRetailCustomer(retailCustomer);
usagePoint.getMeterReadings().add(newMeterReading());
diff --git a/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/web/ModernAuthorizationController.java b/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/web/ModernAuthorizationController.java
index 345b4d3a..32ab20fa 100644
--- a/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/web/ModernAuthorizationController.java
+++ b/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/web/ModernAuthorizationController.java
@@ -128,7 +128,7 @@ public String authorizationCallback(
@GetMapping("/authorizations")
public String authorizationList(ModelMap model, Principal principal) {
try {
- UUID customerId = getCurrentCustomerId(principal);
+ Long customerId = getCurrentCustomerId(principal);
var authorizations = authorizationService.findAllByRetailCustomerId(customerId);
model.put("authorizationList", authorizations);
return "/RetailCustomer/AuthorizationList/index";
@@ -246,7 +246,7 @@ private void markAuthorizationAsFailed(AuthorizationEntity authorization, String
*/
private void importInitialData(Principal principal) {
try {
- UUID customerId = getCurrentCustomerId(principal);
+ Long customerId = getCurrentCustomerId(principal);
usagePointRESTRepository.findAllByRetailCustomerId(customerId);
logger.debug("Successfully imported initial usage point data");
} catch (JAXBException e) {
@@ -259,7 +259,7 @@ private void importInitialData(Principal principal) {
/**
* Gets the current customer ID from the authenticated principal.
*/
- private UUID getCurrentCustomerId(Principal principal) {
+ private Long getCurrentCustomerId(Principal principal) {
if (principal instanceof Authentication auth) {
var customer = retailCustomerService.findByUsername(auth.getName());
if (customer != null) {
diff --git a/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/web/custodian/RetailCustomerController.java b/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/web/custodian/RetailCustomerController.java
index 19032823..d9b0f3a3 100644
--- a/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/web/custodian/RetailCustomerController.java
+++ b/openespi-thirdparty/src/main/java/org/greenbuttonalliance/espi/thirdparty/web/custodian/RetailCustomerController.java
@@ -35,7 +35,6 @@
import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid;
-import java.util.UUID;
@Controller
@PreAuthorize("hasRole('ROLE_CUSTODIAN')")
@@ -88,7 +87,7 @@ public String create(
}
@RequestMapping(value = "/custodian/retailcustomers/{retailCustomerId}", method = RequestMethod.GET)
- public String show(@PathVariable UUID retailCustomerId, ModelMap model) {
+ public String show(@PathVariable Long retailCustomerId, ModelMap model) {
RetailCustomerEntity retailCustomer = service.findById(retailCustomerId);
model.put("retailCustomer", retailCustomer);
return "/custodian/retailcustomers/show";
diff --git a/openespi-thirdparty/src/test/java/org/greenbuttonalliance/espi/thirdparty/integration/repository/UsagePointRESTRepositoryIntegrationTest.java b/openespi-thirdparty/src/test/java/org/greenbuttonalliance/espi/thirdparty/integration/repository/UsagePointRESTRepositoryIntegrationTest.java
index a25700c9..8e484d5b 100644
--- a/openespi-thirdparty/src/test/java/org/greenbuttonalliance/espi/thirdparty/integration/repository/UsagePointRESTRepositoryIntegrationTest.java
+++ b/openespi-thirdparty/src/test/java/org/greenbuttonalliance/espi/thirdparty/integration/repository/UsagePointRESTRepositoryIntegrationTest.java
@@ -82,7 +82,7 @@ public void setUp() {
mockAuthorization.setResourceURI("http://localhost:8080/DataCustodian/espi/1_1/resource/Batch/RetailCustomer/1/UsagePoint");
mockAuthorization.setAccessToken("test-access-token");
- when(authorizationService.findAllByRetailCustomerId(any(UUID.class)))
+ when(authorizationService.findAllByRetailCustomerId(any(Long.class)))
.thenReturn(Collections.singletonList(mockAuthorization));
}
@@ -118,7 +118,7 @@ public void testFindAllByRetailCustomerId_WithValidResponse() throws Exception {
when(responseSpec.bodyToMono(String.class)).thenReturn(Mono.just(xmlResponse));
// Execute test
- List result = usagePointRESTRepository.findAllByRetailCustomerId(UUID.randomUUID());
+ List result = usagePointRESTRepository.findAllByRetailCustomerId(1000001L);
// Verify results
assertNotNull(result);
@@ -130,7 +130,7 @@ public void testFindAllByRetailCustomerId_WithValidResponse() throws Exception {
public void testFindByHashedId_WithExistingUsagePoint() throws Exception {
// This test would require a more complete XML structure to work properly
// For now, we'll test the method exists and handles null gracefully
- UsagePointEntity result = usagePointRESTRepository.findByHashedId(UUID.randomUUID(), "test-hashed-id");
+ UsagePointEntity result = usagePointRESTRepository.findByHashedId(1000004L, "test-hashed-id");
// Should return null when no matching usage points found
assertNull(result);
diff --git a/openespi-thirdparty/src/test/java/org/greenbuttonalliance/espi/thirdparty/repository/impl/MeterReadingRESTRepositoryImplTests.java b/openespi-thirdparty/src/test/java/org/greenbuttonalliance/espi/thirdparty/repository/impl/MeterReadingRESTRepositoryImplTests.java
index a7e73b1a..8d81fe35 100644
--- a/openespi-thirdparty/src/test/java/org/greenbuttonalliance/espi/thirdparty/repository/impl/MeterReadingRESTRepositoryImplTests.java
+++ b/openespi-thirdparty/src/test/java/org/greenbuttonalliance/espi/thirdparty/repository/impl/MeterReadingRESTRepositoryImplTests.java
@@ -43,7 +43,7 @@ public void findByUUID() throws Exception {
repository.setUsagePointRESTRepository(usagePointRESTRepository);
// Create test data with UUID
- UUID retailCustomerId = UUID.randomUUID();
+ Long retailCustomerId = 1000002L;
UUID meterReadingId = UUID.randomUUID();
// Create MeterReadingEntity
diff --git a/openespi-thirdparty/src/test/java/org/greenbuttonalliance/espi/thirdparty/service/impl/MeterReadingServiceImplTests.java b/openespi-thirdparty/src/test/java/org/greenbuttonalliance/espi/thirdparty/service/impl/MeterReadingServiceImplTests.java
index 9a3ae443..7a9daa50 100644
--- a/openespi-thirdparty/src/test/java/org/greenbuttonalliance/espi/thirdparty/service/impl/MeterReadingServiceImplTests.java
+++ b/openespi-thirdparty/src/test/java/org/greenbuttonalliance/espi/thirdparty/service/impl/MeterReadingServiceImplTests.java
@@ -47,7 +47,7 @@ public void before() {
@Test
public void findByUUID_returnsMeterReading() throws JAXBException {
MeterReadingEntity meterReading = Factory.newMeterReading();
- UUID retailCustomerId = UUID.randomUUID();
+ Long retailCustomerId = 1000003L;
UUID meterReadingId = UUID.randomUUID();
when(repository.findByUUID(eq(retailCustomerId), eq(meterReadingId))).thenReturn(