Skip to content

Commit faa0bbb

Browse files
dfcoffinclaude
andauthored
feat: ESPI 4.0 Schema Compliance - Phase 15: BatchList (#82)
This commit implements ESPI 4.0 schema compliance for BatchList entity, migrating from UUID to Long ID with auto-increment and completing the DTO/Mapper implementation for XML marshalling. Key Changes: - Changed BatchListEntity primary key from UUID to Long with IDENTITY generation - Moved batch_lists table to vendor-specific V2 migrations: - H2/MySQL: BIGINT AUTO_INCREMENT PRIMARY KEY - PostgreSQL: BIGSERIAL PRIMARY KEY - Created BatchListDto with JAXB annotations per espi.xsd BatchListType - Created BatchListMapper for Entity-to-DTO conversion (output-only) - Updated BatchListRepository for Long ID type parameter - Created comprehensive test suites: - BatchListDtoTest (16 tests) - BatchListMapperTest (11 tests) - Updated BatchListRepositoryTest for Long ID Technical Details: - BatchList is a simple operational entity (NOT an ESPI resource) - Does NOT extend IdentifiedObject per ESPI 4.0 specification - DTO uses defensive copy pattern to prevent external modification - Mapper follows Phase 14 pattern with @component annotation - All 582 tests passing (including integration tests) Resolves: Issue #28 - Phase 15: BatchList Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 30e7174 commit faa0bbb

11 files changed

Lines changed: 730 additions & 45 deletions

File tree

openespi-common/src/main/java/org/greenbuttonalliance/espi/common/domain/usage/BatchListEntity.java

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,7 @@
2525
import lombok.Getter;
2626
import lombok.NoArgsConstructor;
2727
import lombok.Setter;
28-
import org.hibernate.annotations.JdbcTypeCode;
2928
import org.hibernate.proxy.HibernateProxy;
30-
import org.hibernate.type.SqlTypes;
3129

3230
import java.net.URI;
3331
import java.net.URISyntaxException;
@@ -44,6 +42,7 @@
4442
*
4543
* Note: BatchList does NOT extend IdentifiedObject per ESPI 4.0 specification.
4644
* It is not a top-level resource with selfLink/upLink/relatedLinks.
45+
* Uses Long ID with auto-increment as a simple operational entity.
4746
*/
4847
@Entity
4948
@Table(name = "batch_lists", indexes = {
@@ -57,13 +56,12 @@ public class BatchListEntity {
5756
private static final long serialVersionUID = 1L;
5857

5958
/**
60-
* Primary key identifier.
59+
* Primary key identifier (48-bit auto-increment).
6160
*/
6261
@Id
63-
@GeneratedValue(strategy = GenerationType.UUID)
64-
@JdbcTypeCode(SqlTypes.CHAR)
65-
@Column(length = 36, columnDefinition = "char(36)", updatable = false, nullable = false)
66-
private UUID id;
62+
@GeneratedValue(strategy = GenerationType.IDENTITY)
63+
@Column(updatable = false, nullable = false)
64+
private Long id;
6765

6866
/**
6967
* List of resource URIs for batch processing.
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
*
3+
* Copyright (c) 2025 Green Button Alliance, Inc.
4+
*
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*
18+
*/
19+
20+
package org.greenbuttonalliance.espi.common.dto.usage;
21+
22+
import jakarta.xml.bind.annotation.XmlAccessType;
23+
import jakarta.xml.bind.annotation.XmlAccessorType;
24+
import jakarta.xml.bind.annotation.XmlElement;
25+
import jakarta.xml.bind.annotation.XmlRootElement;
26+
import lombok.Getter;
27+
import lombok.NoArgsConstructor;
28+
29+
import java.util.ArrayList;
30+
import java.util.List;
31+
32+
/**
33+
* DTO for BatchList (output-only for XML marshalling).
34+
*
35+
* List of resource URIs that can be used to GET ESPI resources.
36+
* This supports batch operations by collecting multiple resource URIs
37+
* that can be processed together.
38+
*
39+
* <p>Per ESPI 4.0 XSD (espi.xsd), BatchListType contains:
40+
* <ul>
41+
* <li>resources (xs:anyURI, unbounded) - List of resource URIs</li>
42+
* </ul>
43+
*/
44+
@XmlRootElement(name = "BatchList", namespace = "http://naesb.org/espi")
45+
@XmlAccessorType(XmlAccessType.FIELD)
46+
@Getter
47+
@NoArgsConstructor
48+
public class BatchListDto {
49+
50+
/**
51+
* List of resource URIs for batch processing.
52+
* Each URI points to an ESPI resource that can be retrieved or processed.
53+
*
54+
* Per XSD: minOccurs="0" maxOccurs="unbounded"
55+
*/
56+
@XmlElement(name = "resources", namespace = "http://naesb.org/espi")
57+
private List<String> resources = new ArrayList<>();
58+
59+
/**
60+
* Constructor with resources list.
61+
* Creates a defensive copy to prevent external modification.
62+
*
63+
* @param resources the list of resource URIs
64+
*/
65+
public BatchListDto(List<String> resources) {
66+
this.resources = resources != null ? new ArrayList<>(resources) : new ArrayList<>();
67+
}
68+
69+
/**
70+
* Adds a resource URI to the batch list.
71+
*
72+
* @param resourceUri the resource URI to add
73+
* @return this DTO for fluent chaining
74+
*/
75+
public BatchListDto addResource(String resourceUri) {
76+
if (resourceUri != null && !resourceUri.trim().isEmpty()) {
77+
if (this.resources == null) {
78+
this.resources = new ArrayList<>();
79+
}
80+
this.resources.add(resourceUri.trim());
81+
}
82+
return this;
83+
}
84+
85+
/**
86+
* Adds multiple resource URIs to the batch list.
87+
*
88+
* @param resourceUris the resource URIs to add
89+
* @return this DTO for fluent chaining
90+
*/
91+
public BatchListDto addResources(List<String> resourceUris) {
92+
if (resourceUris != null && !resourceUris.isEmpty()) {
93+
if (this.resources == null) {
94+
this.resources = new ArrayList<>();
95+
}
96+
this.resources.addAll(resourceUris);
97+
}
98+
return this;
99+
}
100+
101+
/**
102+
* Gets the resource count.
103+
*
104+
* @return the number of resources
105+
*/
106+
public int getResourceCount() {
107+
return resources != null ? resources.size() : 0;
108+
}
109+
110+
/**
111+
* Checks if the batch list is empty.
112+
*
113+
* @return true if no resources, false otherwise
114+
*/
115+
public boolean isEmpty() {
116+
return resources == null || resources.isEmpty();
117+
}
118+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
*
3+
* Copyright (c) 2025 Green Button Alliance, Inc.
4+
*
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*
18+
*/
19+
20+
package org.greenbuttonalliance.espi.common.mapper.usage;
21+
22+
import org.greenbuttonalliance.espi.common.domain.usage.BatchListEntity;
23+
import org.greenbuttonalliance.espi.common.dto.usage.BatchListDto;
24+
import org.springframework.stereotype.Component;
25+
26+
import java.util.ArrayList;
27+
import java.util.List;
28+
29+
/**
30+
* Mapper for converting BatchListEntity to BatchListDto.
31+
*
32+
* <p>This is an output-only mapper that creates DTOs for BatchList data.
33+
* BatchList is a simple operational entity containing a list of resource URIs
34+
* for batch processing operations.</p>
35+
*
36+
* <p>Note: This mapper does not support DTO-to-Entity conversion as
37+
* BatchList DTOs are output-only for XML marshalling.</p>
38+
*/
39+
@Component
40+
public class BatchListMapper {
41+
42+
/**
43+
* Converts a BatchListEntity to a BatchListDto.
44+
*
45+
* <p>The mapping is straightforward - only the resources list is transferred
46+
* from entity to DTO. The entity's id and resourceCount fields are not included
47+
* in the DTO per ESPI 4.0 XSD specification.</p>
48+
*
49+
* @param entity the batch list entity to convert
50+
* @return the batch list DTO, or null if entity is null
51+
*/
52+
public BatchListDto toDto(BatchListEntity entity) {
53+
if (entity == null) {
54+
return null;
55+
}
56+
57+
// Create defensive copy of resources list
58+
List<String> resources = entity.getResources() != null
59+
? new ArrayList<>(entity.getResources())
60+
: new ArrayList<>();
61+
62+
return new BatchListDto(resources);
63+
}
64+
65+
/**
66+
* Converts a list of BatchListEntity objects to BatchListDto objects.
67+
*
68+
* <p>This is a convenience method for batch conversions.</p>
69+
*
70+
* @param entities the list of batch list entities to convert
71+
* @return the list of batch list DTOs, empty list if input is null or empty
72+
*/
73+
public List<BatchListDto> toDtoList(List<BatchListEntity> entities) {
74+
if (entities == null || entities.isEmpty()) {
75+
return new ArrayList<>();
76+
}
77+
78+
return entities.stream()
79+
.map(this::toDto)
80+
.toList();
81+
}
82+
}

openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/BatchListRepository.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,14 @@
2323
import org.springframework.data.jpa.repository.JpaRepository;
2424
import org.springframework.stereotype.Repository;
2525

26-
import java.util.UUID;
27-
26+
/**
27+
* Repository for BatchListEntity using Spring Data JPA.
28+
*
29+
* <p>BatchList is a simple operational entity with Long ID (auto-increment).
30+
* Standard CRUD operations are sufficient for this entity.</p>
31+
*/
2832
@Repository
29-
public interface BatchListRepository extends JpaRepository<BatchListEntity, UUID> {
33+
public interface BatchListRepository extends JpaRepository<BatchListEntity, Long> {
3034
// JpaRepository provides: save(), findAll(), findById(), deleteById(), etc.
3135
// BatchList entity has no NamedQueries, so no additional methods are needed
3236
// Standard CRUD operations are sufficient for this simple entity

openespi-common/src/main/resources/db/migration/V1__Create_Base_Tables.sql

Lines changed: 4 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -287,36 +287,7 @@ CREATE INDEX idx_subscription_retail_customer ON subscriptions (retail_customer_
287287
CREATE INDEX idx_subscription_application ON subscriptions (application_information_id);
288288
CREATE INDEX idx_subscription_authorization ON subscriptions (authorization_id);
289289

290-
-- Batch List Table (Independent - no foreign key dependencies)
291-
CREATE TABLE batch_lists
292-
(
293-
id CHAR(36) PRIMARY KEY ,
294-
description VARCHAR(255),
295-
created TIMESTAMP,
296-
updated TIMESTAMP,
297-
published TIMESTAMP,
298-
up_link_rel VARCHAR(255),
299-
up_link_href VARCHAR(1024),
300-
up_link_type VARCHAR(255),
301-
self_link_rel VARCHAR(255),
302-
self_link_href VARCHAR(1024),
303-
self_link_type VARCHAR(255),
304-
305-
-- Batch list specific fields
306-
resource_count INT DEFAULT 0
307-
);
308-
309-
CREATE INDEX idx_batch_list_created ON batch_lists (created);
310-
CREATE INDEX idx_batch_list_resource_count ON batch_lists (resource_count);
311-
CREATE INDEX idx_batch_list_updated ON batch_lists (updated);
312-
313-
-- Batch List Resources Collection Table
314-
CREATE TABLE batch_list_resources
315-
(
316-
batch_list_id CHAR(36) NOT NULL,
317-
resource_uri VARCHAR(512) NOT NULL,
318-
FOREIGN KEY (batch_list_id) REFERENCES batch_lists (id) ON DELETE CASCADE
319-
);
320-
321-
CREATE INDEX idx_batch_list_resources_batch_id ON batch_list_resources (batch_list_id);
322-
CREATE INDEX idx_batch_list_resources_uri ON batch_list_resources (resource_uri);
290+
-- Batch List Table - Moved to vendor-specific V2 migration files
291+
-- BatchList is a simple operational entity (NOT an ESPI resource)
292+
-- Does NOT extend IdentifiedObject, uses Long ID with auto-increment
293+
-- Table creation moved to V2 vendor files due to auto-increment syntax differences

openespi-common/src/main/resources/db/vendor/h2/V2__H2_Specific_Tables.sql

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,3 +584,29 @@ ALTER TABLE subscriptions ADD CONSTRAINT fk_subscription_retail_customer
584584
-- (Column type changed to BIGINT, FK constraint added here after retail_customers table is created)
585585
ALTER TABLE usage_points ADD CONSTRAINT fk_usage_point_retail_customer
586586
FOREIGN KEY (retail_customer_id) REFERENCES retail_customers (id) ON DELETE CASCADE;
587+
588+
-- ================================================================================
589+
-- Batch List Tables (H2-specific with BIGINT AUTO_INCREMENT)
590+
-- ================================================================================
591+
592+
-- Batch List Table
593+
-- BatchList is a simple operational entity (NOT an ESPI resource)
594+
-- Does NOT extend IdentifiedObject, uses Long ID with auto-increment
595+
CREATE TABLE batch_lists
596+
(
597+
id BIGINT AUTO_INCREMENT PRIMARY KEY,
598+
resource_count INT DEFAULT 0
599+
);
600+
601+
CREATE INDEX idx_batch_list_resource_count ON batch_lists (resource_count);
602+
603+
-- Batch List Resources Collection Table
604+
CREATE TABLE batch_list_resources
605+
(
606+
batch_list_id BIGINT NOT NULL,
607+
resource_uri VARCHAR(512) NOT NULL,
608+
FOREIGN KEY (batch_list_id) REFERENCES batch_lists (id) ON DELETE CASCADE
609+
);
610+
611+
CREATE INDEX idx_batch_list_resources_batch_id ON batch_list_resources (batch_list_id);
612+
CREATE INDEX idx_batch_list_resources_uri ON batch_list_resources (resource_uri);

openespi-common/src/main/resources/db/vendor/mysql/V2__MySQL_Specific_Tables.sql

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,3 +582,29 @@ ALTER TABLE subscriptions ADD CONSTRAINT fk_subscription_retail_customer
582582
-- (Column type changed to BIGINT, FK constraint added here after retail_customers table is created)
583583
ALTER TABLE usage_points ADD CONSTRAINT fk_usage_point_retail_customer
584584
FOREIGN KEY (retail_customer_id) REFERENCES retail_customers (id) ON DELETE CASCADE;
585+
586+
-- ================================================================================
587+
-- Batch List Tables (MySQL-specific with BIGINT AUTO_INCREMENT)
588+
-- ================================================================================
589+
590+
-- Batch List Table
591+
-- BatchList is a simple operational entity (NOT an ESPI resource)
592+
-- Does NOT extend IdentifiedObject, uses Long ID with auto-increment
593+
CREATE TABLE batch_lists
594+
(
595+
id BIGINT AUTO_INCREMENT PRIMARY KEY,
596+
resource_count INT DEFAULT 0
597+
);
598+
599+
CREATE INDEX idx_batch_list_resource_count ON batch_lists (resource_count);
600+
601+
-- Batch List Resources Collection Table
602+
CREATE TABLE batch_list_resources
603+
(
604+
batch_list_id BIGINT NOT NULL,
605+
resource_uri VARCHAR(512) NOT NULL,
606+
FOREIGN KEY (batch_list_id) REFERENCES batch_lists (id) ON DELETE CASCADE
607+
);
608+
609+
CREATE INDEX idx_batch_list_resources_batch_id ON batch_list_resources (batch_list_id);
610+
CREATE INDEX idx_batch_list_resources_uri ON batch_list_resources (resource_uri);

openespi-common/src/main/resources/db/vendor/postgres/V2__PostgreSQL_Specific_Tables.sql

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,3 +571,29 @@ ALTER TABLE subscriptions ADD CONSTRAINT fk_subscription_retail_customer
571571
-- (Column type changed to BIGINT, FK constraint added here after retail_customers table is created)
572572
ALTER TABLE usage_points ADD CONSTRAINT fk_usage_point_retail_customer
573573
FOREIGN KEY (retail_customer_id) REFERENCES retail_customers (id) ON DELETE CASCADE;
574+
575+
-- ================================================================================
576+
-- Batch List Tables (PostgreSQL-specific with BIGSERIAL)
577+
-- ================================================================================
578+
579+
-- Batch List Table
580+
-- BatchList is a simple operational entity (NOT an ESPI resource)
581+
-- Does NOT extend IdentifiedObject, uses Long ID with auto-increment
582+
CREATE TABLE batch_lists
583+
(
584+
id BIGSERIAL PRIMARY KEY,
585+
resource_count INTEGER DEFAULT 0
586+
);
587+
588+
CREATE INDEX idx_batch_list_resource_count ON batch_lists (resource_count);
589+
590+
-- Batch List Resources Collection Table
591+
CREATE TABLE batch_list_resources
592+
(
593+
batch_list_id BIGINT NOT NULL,
594+
resource_uri VARCHAR(512) NOT NULL,
595+
FOREIGN KEY (batch_list_id) REFERENCES batch_lists (id) ON DELETE CASCADE
596+
);
597+
598+
CREATE INDEX idx_batch_list_resources_batch_id ON batch_list_resources (batch_list_id);
599+
CREATE INDEX idx_batch_list_resources_uri ON batch_list_resources (resource_uri);

0 commit comments

Comments
 (0)