From 94be32fed011473883ac4a211efbaaef6df68354 Mon Sep 17 00:00:00 2001 From: Don Strawsburg Date: Wed, 14 Jan 2026 16:19:50 -0500 Subject: [PATCH] changes to build so we can create the screen that is missing --- .idea/dataSources.xml | 88 ++++++++----- .mvn/wrapper/maven-wrapper.properties | 19 --- README.md | 2 +- .../controllers/api/v1/BuildController.java | 3 +- .../battlbuilder/domain/package-info.java | 2 + .../battlbuilder/imports/package-info.java | 11 ++ .../battlbuilder/mapper/BuildItemMapper.java | 99 ++++++++++++++ .../battlbuilder/mapper/BuildMapper.java | 123 ++++++++++++++++++ .../goforward/battlbuilder/model/Build.java | 25 ++++ .../battlbuilder/model/BuildItem.java | 2 + .../goforward/battlbuilder/model/Product.java | 2 +- src/main/resources/application.properties | 3 + wget-log | 0 13 files changed, 327 insertions(+), 52 deletions(-) delete mode 100644 .mvn/wrapper/maven-wrapper.properties create mode 100644 src/main/java/group/goforward/battlbuilder/domain/package-info.java create mode 100644 src/main/java/group/goforward/battlbuilder/imports/package-info.java create mode 100644 src/main/java/group/goforward/battlbuilder/mapper/BuildItemMapper.java create mode 100644 src/main/java/group/goforward/battlbuilder/mapper/BuildMapper.java delete mode 100644 wget-log diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml index f1e7a08..9b1fd28 100644 --- a/.idea/dataSources.xml +++ b/.idea/dataSources.xml @@ -1,31 +1,59 @@ - - - - - postgresql - true - org.postgresql.Driver - jdbc:postgresql://r710.dev.gofwd.group:5433/postgres - - - - - - $ProjectFileDir$ - - - postgresql - true - true - $PROJECT_DIR$/src/main/resources/application.properties - org.postgresql.Driver - jdbc:postgresql://r710.gofwd.group:5433/ss_builder - - - - - - $ProjectFileDir$ - - + + + + + postgresql + true + true + $PROJECT_DIR$/src/main/resources/application.properties + org.postgresql.Driver + jdbc:postgresql://r710.dev.gofwd.group:5433/ss_builder + + + + + + $ProjectFileDir$ + + + postgresql + true + true + $PROJECT_DIR$/src/main/resources/application.properties + org.postgresql.Driver + jdbc:postgresql://r710.dev.gofwd.group:5433/ss_builder + + + + + + $ProjectFileDir$ + + + postgresql + true + org.postgresql.Driver + jdbc:postgresql://r710.dev.gofwd.group:5433/postgres + + + + + + $ProjectFileDir$ + + + postgresql + true + true + $PROJECT_DIR$/src/main/resources/application.properties + org.postgresql.Driver + jdbc:postgresql://r710.gofwd.group:5433/ss_builder + + + + + + $ProjectFileDir$ + + \ No newline at end of file diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index b2f4fc0..0000000 --- a/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1,19 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you 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. -wrapperVersion=3.3.2 -distributionType=only-script -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip diff --git a/README.md b/README.md index 14785e2..52e9b27 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ The frontend Builder depends on this backend for: ## Tech Stack - **Spring Boot 3.x** -- **Java 17** +- **Java 21** - **PostgreSQL** - **Hibernate (JPA)** - **HikariCP** diff --git a/src/main/java/group/goforward/battlbuilder/controllers/api/v1/BuildController.java b/src/main/java/group/goforward/battlbuilder/controllers/api/v1/BuildController.java index 22e6744..a06107c 100644 --- a/src/main/java/group/goforward/battlbuilder/controllers/api/v1/BuildController.java +++ b/src/main/java/group/goforward/battlbuilder/controllers/api/v1/BuildController.java @@ -2,6 +2,7 @@ package group.goforward.battlbuilder.controllers.api.v1; import group.goforward.battlbuilder.model.Build; import group.goforward.battlbuilder.repos.build.BuildRepository; +import group.goforward.battlbuilder.web.dto.build.BuildDto; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -10,7 +11,7 @@ import java.util.List; @RestController -@RequestMapping("/v1/api/builds") +@RequestMapping("/api/builds") public class BuildController { @Autowired private BuildRepository repo; diff --git a/src/main/java/group/goforward/battlbuilder/domain/package-info.java b/src/main/java/group/goforward/battlbuilder/domain/package-info.java new file mode 100644 index 0000000..c0a6ebf --- /dev/null +++ b/src/main/java/group/goforward/battlbuilder/domain/package-info.java @@ -0,0 +1,2 @@ +package group.goforward.battlbuilder.domain; +; \ No newline at end of file diff --git a/src/main/java/group/goforward/battlbuilder/imports/package-info.java b/src/main/java/group/goforward/battlbuilder/imports/package-info.java new file mode 100644 index 0000000..1f8f92c --- /dev/null +++ b/src/main/java/group/goforward/battlbuilder/imports/package-info.java @@ -0,0 +1,11 @@ +/** + * Imports of data from feeds + * + *

The main entry point for managing the inventory is the + * {@link group.goforward.battlbuilder.BattlBuilderApplication} class.

+ * + * @since 1.0 + * @author Sean Strawsburg + * @version 1.1 + */ +package group.goforward.battlbuilder.imports; \ No newline at end of file diff --git a/src/main/java/group/goforward/battlbuilder/mapper/BuildItemMapper.java b/src/main/java/group/goforward/battlbuilder/mapper/BuildItemMapper.java new file mode 100644 index 0000000..cc89646 --- /dev/null +++ b/src/main/java/group/goforward/battlbuilder/mapper/BuildItemMapper.java @@ -0,0 +1,99 @@ +package group.goforward.battlbuilder.mapper; + +import group.goforward.battlbuilder.model.BuildItem; +import group.goforward.battlbuilder.model.Product; +import group.goforward.battlbuilder.web.dto.build.BuildItemDto; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public final class BuildItemMapper { + + private BuildItemMapper() { + // utility class + } + + // --------------------------------------------------------- + // BuildItem → BuildItemDto + // --------------------------------------------------------- + public static BuildItemDto toDto(BuildItem item) { + if (item == null) { + return null; + } + + BuildItemDto dto = new BuildItemDto(); + + dto.setId(item.getId() != null ? String.valueOf(item.getId()) : null); + dto.setUuid(item.getUuid()); + + dto.setSlot(item.getSlot()); + dto.setPosition(item.getPosition()); + dto.setQuantity(item.getQuantity()); + + Product product = item.getProduct(); + if (product != null) { + dto.setProductId(product.getId() != null ? String.valueOf(product.getId()) : null); + dto.setProductName(product.getName()); + dto.setProductBrand( + product.getBrand() != null ? product.getBrand().getName() : null + ); + dto.setProductImageUrl(product.getMainImageUrl()); + // bestPrice remains a concern of a pricing service / aggregator + } + + return dto; + } + + // --------------------------------------------------------- + // BuildItemDto → BuildItem + // --------------------------------------------------------- + public static BuildItem toEntity(BuildItemDto dto) { + if (dto == null) { + return null; + } + + BuildItem entity = new BuildItem(); + + if (dto.getId() != null && !dto.getId().isBlank()) { + try { + entity.setId(Integer.valueOf(dto.getId())); + } catch (NumberFormatException ignored) { + // leave id null if parsing fails + } + } + + entity.setUuid(dto.getUuid()); + entity.setSlot(dto.getSlot()); + entity.setPosition(dto.getPosition()); + entity.setQuantity(dto.getQuantity()); + + // Product + Build references should be set by the service layer: + // - resolve product via ProductRepository using dto.getProductId() + // - assign Build via build.addItem(item) or item.setBuild(build) + return entity; + } + + // --------------------------------------------------------- + // Collection helpers + // --------------------------------------------------------- + public static List toDtoList(List items) { + if (items == null) { + return null; + } + return items.stream() + .filter(Objects::nonNull) + .map(BuildItemMapper::toDto) + .collect(Collectors.toList()); + } + + public static List toEntityList(List dtos) { + if (dtos == null) { + return null; + } + return dtos.stream() + .filter(Objects::nonNull) + .map(BuildItemMapper::toEntity) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/group/goforward/battlbuilder/mapper/BuildMapper.java b/src/main/java/group/goforward/battlbuilder/mapper/BuildMapper.java new file mode 100644 index 0000000..28a41c1 --- /dev/null +++ b/src/main/java/group/goforward/battlbuilder/mapper/BuildMapper.java @@ -0,0 +1,123 @@ +package group.goforward.battlbuilder.mapper; + +import group.goforward.battlbuilder.model.Build; +import group.goforward.battlbuilder.model.BuildItem; +import group.goforward.battlbuilder.model.Product; +import group.goforward.battlbuilder.web.dto.build.BuildDto; +import group.goforward.battlbuilder.web.dto.build.BuildItemDto; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public final class BuildMapper { + + private BuildMapper() { + // utility class + } + + // --------------------------------------------------------- + // Build → BuildDto + // --------------------------------------------------------- + public static BuildDto toDto(Build entity) { + if (entity == null) { + return null; + } + + BuildDto dto = new BuildDto(); + + dto.setId(entity.getId() != null ? String.valueOf(entity.getId()) : null); + dto.setUuid(entity.getUuid()); + + dto.setTitle(entity.getTitle()); + dto.setDescription(entity.getDescription()); + dto.setIsPublic(entity.getIsPublic()); + + dto.setCreatedAt(entity.getCreatedAt()); + dto.setUpdatedAt(entity.getUpdatedAt()); + + if (entity.getItems() != null) { + dto.setItems( + entity.getItems().stream() + .filter(Objects::nonNull) + .map(BuildMapper::toItemDto) + .collect(Collectors.toList()) + ); + } + + return dto; + } + + // --------------------------------------------------------- + // BuildDto → Build + // --------------------------------------------------------- + public static Build toEntity(BuildDto dto) { + if (dto == null) { + return null; + } + + Build entity = new Build(); + + if (dto.getId() != null && !dto.getId().isBlank()) { + try { + entity.setId(Integer.valueOf(dto.getId())); + } catch (NumberFormatException ignored) { + // leave id null if it can't be parsed + } + } + + entity.setUuid(dto.getUuid()); + + entity.setTitle(dto.getTitle()); + entity.setDescription(dto.getDescription()); + entity.setIsPublic(dto.getIsPublic()); + + entity.setCreatedAt(dto.getCreatedAt()); + entity.setUpdatedAt(dto.getUpdatedAt()); + + // Items are typically managed separately (service layer), + // so we don't automatically map DTO items back to entities here. + // If you want that, wire in a factory/lookup for Product and hydrate BuildItem. + return entity; + } + + // --------------------------------------------------------- + // BuildItem → BuildItemDto + // --------------------------------------------------------- + private static BuildItemDto toItemDto(BuildItem item) { + BuildItemDto dto = new BuildItemDto(); + + dto.setId(item.getId() != null ? String.valueOf(item.getId()) : null); + dto.setUuid(item.getUuid()); + + dto.setSlot(item.getSlot()); + dto.setPosition(item.getPosition()); + dto.setQuantity(item.getQuantity()); + + Product product = item.getProduct(); + if (product != null) { + dto.setProductId(product.getId() != null ? String.valueOf(product.getId()) : null); + dto.setProductName(product.getName()); + dto.setProductBrand( + product.getBrand() != null ? product.getBrand().getName() : null + ); + dto.setProductImageUrl(product.getMainImageUrl()); + // bestPrice is intentionally left for service-layer aggregation + } + + return dto; + } + + // --------------------------------------------------------- + // Collection helpers + // --------------------------------------------------------- + public static List toDtoList(List entities) { + if (entities == null) { + return null; + } + return entities.stream() + .filter(Objects::nonNull) + .map(BuildMapper::toDto) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/group/goforward/battlbuilder/model/Build.java b/src/main/java/group/goforward/battlbuilder/model/Build.java index 62de704..9c49218 100644 --- a/src/main/java/group/goforward/battlbuilder/model/Build.java +++ b/src/main/java/group/goforward/battlbuilder/model/Build.java @@ -1,10 +1,13 @@ package group.goforward.battlbuilder.model; +import com.fasterxml.jackson.annotation.JsonManagedReference; import jakarta.persistence.*; import org.hibernate.annotations.ColumnDefault; import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; @Entity @@ -60,6 +63,10 @@ public class Build { @Column(name = "deleted_at") private OffsetDateTime deletedAt; + @JsonManagedReference + @OneToMany(mappedBy = "build", cascade = CascadeType.ALL, orphanRemoval = true) + private List items = new ArrayList<>(); + // ----------------------------------------------------- // Hibernate lifecycle // ----------------------------------------------------- @@ -106,4 +113,22 @@ public class Build { public OffsetDateTime getDeletedAt() { return deletedAt; } public void setDeletedAt(OffsetDateTime deletedAt) { this.deletedAt = deletedAt; } + + public List getItems() { + return items; + } + + public void setItems(List items) { + this.items = items; + } + + public void addItem(BuildItem item) { + items.add(item); + item.setBuild(this); + } + + public void removeItem(BuildItem item) { + items.remove(item); + item.setBuild(null); + } } \ No newline at end of file diff --git a/src/main/java/group/goforward/battlbuilder/model/BuildItem.java b/src/main/java/group/goforward/battlbuilder/model/BuildItem.java index fc5d523..c2eea60 100644 --- a/src/main/java/group/goforward/battlbuilder/model/BuildItem.java +++ b/src/main/java/group/goforward/battlbuilder/model/BuildItem.java @@ -1,5 +1,6 @@ package group.goforward.battlbuilder.model; +import com.fasterxml.jackson.annotation.JsonBackReference; import jakarta.persistence.*; import jakarta.validation.constraints.NotNull; import org.hibernate.annotations.ColumnDefault; @@ -22,6 +23,7 @@ public class BuildItem { @Column(name = "uuid", nullable = false) private UUID uuid; + @JsonBackReference @NotNull @ManyToOne(fetch = FetchType.LAZY, optional = false) @OnDelete(action = OnDeleteAction.CASCADE) diff --git a/src/main/java/group/goforward/battlbuilder/model/Product.java b/src/main/java/group/goforward/battlbuilder/model/Product.java index 96714ab..6bc5c4f 100644 --- a/src/main/java/group/goforward/battlbuilder/model/Product.java +++ b/src/main/java/group/goforward/battlbuilder/model/Product.java @@ -41,7 +41,7 @@ public class Product { private UUID uuid; @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "brand_id", nullable = false) + @JoinColumn(name = "brand_id", nullable = true) private Brand brand; @Column(name = "name", nullable = false) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index dbae7ff..a22737b 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -79,3 +79,6 @@ app.beta.invite.tokenMinutes=30 ai.minConfidence=0.75 ai.openai.apiKey=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 ai.openai.model=gpt-4.1-mini + + +spring.jackson.serialization.fail-on-empty-beans=false \ No newline at end of file diff --git a/wget-log b/wget-log deleted file mode 100644 index e69de29..0000000