diff --git a/src/main/java/group/goforward/battlbuilder/BattlBuilderApplication.java b/src/main/java/group/goforward/battlbuilder/BattlBuilderApplication.java index b190dda..a45b67d 100644 --- a/src/main/java/group/goforward/battlbuilder/BattlBuilderApplication.java +++ b/src/main/java/group/goforward/battlbuilder/BattlBuilderApplication.java @@ -1,13 +1,16 @@ package group.goforward.battlbuilder; +import group.goforward.battlbuilder.config.ShortLinksProperties; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cache.annotation.EnableCaching; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; @SpringBootApplication @EnableCaching +@EnableConfigurationProperties(ShortLinksProperties.class) @EntityScan(basePackages = { "group.goforward.battlbuilder.model", "group.goforward.battlbuilder.enrichment" diff --git a/src/main/java/group/goforward/battlbuilder/config/SecurityConfig.java b/src/main/java/group/goforward/battlbuilder/config/SecurityConfig.java index 4f6d62f..1264a60 100644 --- a/src/main/java/group/goforward/battlbuilder/config/SecurityConfig.java +++ b/src/main/java/group/goforward/battlbuilder/config/SecurityConfig.java @@ -1,4 +1,4 @@ -package group.goforward.battlbuilder.configuration; +package group.goforward.battlbuilder.config; import group.goforward.battlbuilder.security.JwtAuthenticationFilter; import org.springframework.context.annotation.Bean; @@ -50,6 +50,10 @@ public class SecurityConfig { .requestMatchers(HttpMethod.GET, "/api/v1/builds").permitAll() .requestMatchers(HttpMethod.GET, "/api/v1/builds/*").permitAll() + // Short links (public redirect) + .requestMatchers(HttpMethod.GET, "/go/**").permitAll() + .requestMatchers(HttpMethod.HEAD, "/go/**").permitAll() + // ---------------------------- // Protected // ---------------------------- diff --git a/src/main/java/group/goforward/battlbuilder/config/ShortLinksProperties.java b/src/main/java/group/goforward/battlbuilder/config/ShortLinksProperties.java new file mode 100644 index 0000000..cebda95 --- /dev/null +++ b/src/main/java/group/goforward/battlbuilder/config/ShortLinksProperties.java @@ -0,0 +1,36 @@ +package group.goforward.battlbuilder.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "shortlinks") +public class ShortLinksProperties { + + /** + * Master switch to enable short links. + */ + private boolean enabled = true; + + /** + * The public base URL used when generating links (differs in dev vs prod). + * Examples: + * - http://localhost:8080 + * - https://bb.ooo + */ + private String publicBaseUrl = "http://localhost:8080"; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String getPublicBaseUrl() { + return publicBaseUrl; + } + + public void setPublicBaseUrl(String publicBaseUrl) { + this.publicBaseUrl = publicBaseUrl; + } +} \ No newline at end of file diff --git a/src/main/java/group/goforward/battlbuilder/controllers/GoController.java b/src/main/java/group/goforward/battlbuilder/controllers/GoController.java new file mode 100644 index 0000000..6261b70 --- /dev/null +++ b/src/main/java/group/goforward/battlbuilder/controllers/GoController.java @@ -0,0 +1,38 @@ +package group.goforward.battlbuilder.controllers; + +import group.goforward.battlbuilder.model.ShortLink; +import group.goforward.battlbuilder.repos.ShortLinkRepository; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +@Controller +public class GoController { + + private final ShortLinkRepository repo; + + public GoController(ShortLinkRepository repo) { + this.repo = repo; + } + + @GetMapping("/go/{code}") + public ResponseEntity go(@PathVariable String code) { + ShortLink link = repo.findByCodeAndIsActiveTrue(code).orElse(null); + if (link == null) return ResponseEntity.notFound().build(); + + String dest = link.getDestinationUrl(); + + // Future: BUILD share links can compute a frontend URL here + // if ("BUILD".equalsIgnoreCase(link.getType()) && link.getBuildUuid() != null) { + // dest = "https://app.battlbuilder.com/build/" + link.getBuildUuid(); + // } + + if (dest == null || dest.isBlank()) return ResponseEntity.notFound().build(); + + return ResponseEntity.status(302) + .header(HttpHeaders.LOCATION, dest) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/group/goforward/battlbuilder/controllers/ShortLinkRedirectController.java b/src/main/java/group/goforward/battlbuilder/controllers/ShortLinkRedirectController.java new file mode 100644 index 0000000..beba4cb --- /dev/null +++ b/src/main/java/group/goforward/battlbuilder/controllers/ShortLinkRedirectController.java @@ -0,0 +1,48 @@ +package group.goforward.battlbuilder.controllers; + +import group.goforward.battlbuilder.repos.ShortLinkRepository; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.server.ResponseStatusException; + +import java.net.URI; + +@RestController +public class ShortLinkRedirectController { + + private final ShortLinkRepository repo; + + public ShortLinkRedirectController(ShortLinkRepository repo) { + this.repo = repo; + } + + @GetMapping("/go/{code}") + public ResponseEntity go(@PathVariable String code) { + var link = repo.findByCodeAndIsActiveTrue(code) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + + if (!"BUY".equals(link.getType()) || link.getDestinationUrl() == null) { + throw new ResponseStatusException(HttpStatus.NOT_FOUND); + } + + return ResponseEntity.status(HttpStatus.FOUND) + .location(URI.create(link.getDestinationUrl())) + .build(); + } + + @GetMapping("/b/{code}") + public ResponseEntity build(@PathVariable String code) { + var link = repo.findByCodeAndIsActiveTrue(code) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + + if (!"BUILD".equals(link.getType()) || link.getBuildUuid() == null) { + throw new ResponseStatusException(HttpStatus.NOT_FOUND); + } + + // Adjust to your actual frontend route + return ResponseEntity.status(HttpStatus.FOUND) + .location(URI.create("/builds/" + link.getBuildUuid())) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/group/goforward/battlbuilder/model/ShortLink.java b/src/main/java/group/goforward/battlbuilder/model/ShortLink.java new file mode 100644 index 0000000..66a157c --- /dev/null +++ b/src/main/java/group/goforward/battlbuilder/model/ShortLink.java @@ -0,0 +1,74 @@ +package group.goforward.battlbuilder.model; + +import jakarta.persistence.*; +import org.hibernate.annotations.ColumnDefault; +import org.hibernate.annotations.UuidGenerator; + +import java.time.OffsetDateTime; +import java.util.UUID; + +@Entity +@Table( + name = "short_links", + indexes = { + @Index(name = "idx_short_links_code", columnList = "code"), + @Index(name = "idx_short_links_type_offer", columnList = "type,product_offer_id"), + @Index(name = "idx_short_links_type_build", columnList = "type,build_uuid") + } +) +public class ShortLink { + + @Id + @GeneratedValue + @UuidGenerator + @Column(name = "id", nullable = false) + private UUID id; + + @Column(name = "code", nullable = false, unique = true, length = 12) + private String code; + + // BUY | BUILD + @Column(name = "type", nullable = false, length = 20) + private String type; + + @Column(name = "destination_url") + private String destinationUrl; + + @Column(name = "product_offer_id") + private Integer productOfferId; + + @Column(name = "build_uuid") + private UUID buildUuid; + + @ColumnDefault("true") + @Column(name = "is_active", nullable = false) + private Boolean isActive = true; + + @ColumnDefault("now()") + @Column(name = "created_at", nullable = false) + private OffsetDateTime createdAt = OffsetDateTime.now(); + + public UUID getId() { return id; } + public void setId(UUID id) { this.id = id; } + + public String getCode() { return code; } + public void setCode(String code) { this.code = code; } + + public String getType() { return type; } + public void setType(String type) { this.type = type; } + + public String getDestinationUrl() { return destinationUrl; } + public void setDestinationUrl(String destinationUrl) { this.destinationUrl = destinationUrl; } + + public Integer getProductOfferId() { return productOfferId; } + public void setProductOfferId(Integer productOfferId) { this.productOfferId = productOfferId; } + + public UUID getBuildUuid() { return buildUuid; } + public void setBuildUuid(UUID buildUuid) { this.buildUuid = buildUuid; } + + public Boolean getIsActive() { return isActive; } + public void setIsActive(Boolean active) { isActive = active; } + + public OffsetDateTime getCreatedAt() { return createdAt; } + public void setCreatedAt(OffsetDateTime createdAt) { this.createdAt = createdAt; } +} \ No newline at end of file diff --git a/src/main/java/group/goforward/battlbuilder/repos/ShortLinkRepository.java b/src/main/java/group/goforward/battlbuilder/repos/ShortLinkRepository.java new file mode 100644 index 0000000..58dcb92 --- /dev/null +++ b/src/main/java/group/goforward/battlbuilder/repos/ShortLinkRepository.java @@ -0,0 +1,17 @@ +package group.goforward.battlbuilder.repos; + +import group.goforward.battlbuilder.model.ShortLink; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; +import java.util.UUID; + +public interface ShortLinkRepository extends JpaRepository { + + Optional findByCodeAndIsActiveTrue(String code); + + Optional findByTypeAndProductOfferId(String type, Integer productOfferId); + + // Future short URL support for build sharing + Optional findByTypeAndBuildUuid(String type, UUID buildUuid); +} \ No newline at end of file diff --git a/src/main/java/group/goforward/battlbuilder/services/ShortLinkService.java b/src/main/java/group/goforward/battlbuilder/services/ShortLinkService.java new file mode 100644 index 0000000..12b987f --- /dev/null +++ b/src/main/java/group/goforward/battlbuilder/services/ShortLinkService.java @@ -0,0 +1,67 @@ +package group.goforward.battlbuilder.services; + +import group.goforward.battlbuilder.config.ShortLinksProperties; +import group.goforward.battlbuilder.model.ShortLink; +import group.goforward.battlbuilder.repos.ShortLinkRepository; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.stereotype.Service; + +import java.security.SecureRandom; +import java.util.UUID; + +@Service +public class ShortLinkService { + + private static final String TYPE_BUY = "BUY"; + private static final String ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + private static final SecureRandom RNG = new SecureRandom(); + + private final ShortLinkRepository repo; + private final ShortLinksProperties props; + + public ShortLinkService(ShortLinkRepository repo, ShortLinksProperties props) { + this.repo = repo; + this.props = props; + } + + public String shortBuyUrlForOffer(Integer offerId, String destinationUrl) { + if (!props.isEnabled()) return destinationUrl; + if (offerId == null || destinationUrl == null || destinationUrl.isBlank()) return destinationUrl; + + var existing = repo.findByTypeAndProductOfferId(TYPE_BUY, offerId).orElse(null); + if (existing != null) return publicUrl(existing.getCode()); + + for (int attempt = 0; attempt < 5; attempt++) { + try { + ShortLink link = new ShortLink(); + link.setType(TYPE_BUY); + link.setProductOfferId(offerId); + link.setDestinationUrl(destinationUrl); + link.setCode(code(8)); + + repo.saveAndFlush(link); // flush forces the constraint check *here* + return publicUrl(link.getCode()); + + } catch (DataIntegrityViolationException ignored) { + // another thread/process created it first + var raced = repo.findByTypeAndProductOfferId(TYPE_BUY, offerId).orElse(null); + if (raced != null) return publicUrl(raced.getCode()); + } + } + + return destinationUrl; + } + + private String publicUrl(String code) { + String base = props.getPublicBaseUrl(); + if (base == null) base = ""; + base = base.endsWith("/") ? base.substring(0, base.length() - 1) : base; + return base + "/go/" + code; + } + + private String code(int len) { + StringBuilder sb = new StringBuilder(len); + for (int i = 0; i < len; i++) sb.append(ALPHABET.charAt(RNG.nextInt(ALPHABET.length()))); + return sb.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/group/goforward/battlbuilder/services/impl/CatalogQueryServiceImpl.java b/src/main/java/group/goforward/battlbuilder/services/impl/CatalogQueryServiceImpl.java index cd945ba..8d5c69c 100644 --- a/src/main/java/group/goforward/battlbuilder/services/impl/CatalogQueryServiceImpl.java +++ b/src/main/java/group/goforward/battlbuilder/services/impl/CatalogQueryServiceImpl.java @@ -6,10 +6,10 @@ import group.goforward.battlbuilder.repos.ProductOfferRepository; import group.goforward.battlbuilder.repos.ProductRepository; import group.goforward.battlbuilder.repos.spec.CatalogProductSpecifications; import group.goforward.battlbuilder.services.CatalogQueryService; +import group.goforward.battlbuilder.services.ShortLinkService; import group.goforward.battlbuilder.web.dto.ProductSummaryDto; import group.goforward.battlbuilder.web.dto.catalog.CatalogProductIdsRequest; import group.goforward.battlbuilder.web.mapper.ProductMapper; - import org.springframework.data.domain.*; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; @@ -24,11 +24,16 @@ public class CatalogQueryServiceImpl implements CatalogQueryService { private final ProductRepository productRepository; private final ProductOfferRepository productOfferRepository; + private final ShortLinkService shortLinkService; - public CatalogQueryServiceImpl(ProductRepository productRepository, - ProductOfferRepository productOfferRepository) { + public CatalogQueryServiceImpl( + ProductRepository productRepository, + ProductOfferRepository productOfferRepository, + ShortLinkService shortLinkService + ) { this.productRepository = productRepository; this.productOfferRepository = productOfferRepository; + this.shortLinkService = shortLinkService; } @Override @@ -81,7 +86,11 @@ public class CatalogQueryServiceImpl implements CatalogQueryService { ProductOffer best = bestOfferByProductId.get(p.getId()); BigDecimal price = best != null ? best.getEffectivePrice() : null; String buyUrl = best != null ? best.getBuyUrl() : null; - return ProductMapper.toSummary(p, price, buyUrl); + String buyShortUrl = best != null + ? shortLinkService.shortBuyUrlForOffer(best.getId(), buyUrl) + : null; + + return ProductMapper.toSummary(p, price, buyUrl, buyShortUrl); }).toList(); return new PageImpl<>(dtos, pageable, page.getTotalElements()); @@ -111,8 +120,11 @@ public class CatalogQueryServiceImpl implements CatalogQueryService { ProductOffer best = bestOfferByProductId.get(id); BigDecimal price = best != null ? best.getEffectivePrice() : null; String buyUrl = best != null ? best.getBuyUrl() : null; + String buyShortUrl = best != null + ? shortLinkService.shortBuyUrlForOffer(best.getId(), buyUrl) + : null; - out.add(ProductMapper.toSummary(p, price, buyUrl)); + out.add(ProductMapper.toSummary(p, price, buyUrl, buyShortUrl)); } return out; @@ -135,7 +147,6 @@ public class CatalogQueryServiceImpl implements CatalogQueryService { continue; } - // ---- ranking rules (in order) ---- // 1) prefer in-stock boolean oStock = Boolean.TRUE.equals(o.getInStock()); boolean cStock = Boolean.TRUE.equals(current.getInStock()); @@ -182,14 +193,12 @@ public class CatalogQueryServiceImpl implements CatalogQueryService { int page = pageable.getPageNumber(); int requested = pageable.getPageSize(); - int size = Math.min(Math.max(requested, 1), 48); // ✅ hard cap + int size = Math.min(Math.max(requested, 1), 48); - // Default sort if none provided if (pageable.getSort() == null || pageable.getSort().isUnsorted()) { return PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "updatedAt")); } - // Only allow safe sorts (for now) Sort.Order first = pageable.getSort().stream().findFirst().orElse(null); if (first == null) { return PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "updatedAt")); @@ -198,9 +207,6 @@ public class CatalogQueryServiceImpl implements CatalogQueryService { String prop = first.getProperty(); Sort.Direction dir = first.getDirection(); - // IMPORTANT: - // If you're still using JPA Specifications (Product entity), you can only sort by Product fields. - // Once you switch to the native "best offer" query, you can allow "price" and "brand" sorts. return switch (prop) { case "updatedAt", "createdAt", "name" -> PageRequest.of(page, size, Sort.by(dir, prop)); default -> PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "updatedAt")); diff --git a/src/main/java/group/goforward/battlbuilder/services/impl/ProductQueryServiceImpl.java b/src/main/java/group/goforward/battlbuilder/services/impl/ProductQueryServiceImpl.java index c4ac5bd..1f89e35 100644 --- a/src/main/java/group/goforward/battlbuilder/services/impl/ProductQueryServiceImpl.java +++ b/src/main/java/group/goforward/battlbuilder/services/impl/ProductQueryServiceImpl.java @@ -5,38 +5,38 @@ import group.goforward.battlbuilder.model.ProductOffer; import group.goforward.battlbuilder.repos.ProductOfferRepository; import group.goforward.battlbuilder.repos.ProductRepository; import group.goforward.battlbuilder.services.ProductQueryService; +import group.goforward.battlbuilder.services.ShortLinkService; import group.goforward.battlbuilder.web.dto.ProductOfferDto; import group.goforward.battlbuilder.web.dto.ProductSummaryDto; -import group.goforward.battlbuilder.web.mapper.ProductMapper; import group.goforward.battlbuilder.web.dto.catalog.ProductSort; - -import java.math.BigDecimal; -import java.util.*; -import java.util.stream.Collectors; +import group.goforward.battlbuilder.web.mapper.ProductMapper; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; - -import java.util.stream.Collectors; +import java.math.BigDecimal; import java.util.Collections; -import java.util.Map; +import java.util.Comparator; import java.util.List; -import java.util.ArrayList; +import java.util.Map; +import java.util.stream.Collectors; @Service public class ProductQueryServiceImpl implements ProductQueryService { private final ProductRepository productRepository; private final ProductOfferRepository productOfferRepository; + private final ShortLinkService shortLinkService; public ProductQueryServiceImpl( ProductRepository productRepository, - ProductOfferRepository productOfferRepository + ProductOfferRepository productOfferRepository, + ShortLinkService shortLinkService ) { this.productRepository = productRepository; this.productOfferRepository = productOfferRepository; + this.shortLinkService = shortLinkService; } @Override @@ -58,7 +58,6 @@ public class ProductQueryServiceImpl implements ProductQueryService { List productIds = products.stream().map(Product::getId).toList(); - // ✅ canonical repo method List allOffers = productOfferRepository.findByProduct_IdIn(productIds); Map> offersByProductId = allOffers.stream() @@ -74,8 +73,11 @@ public class ProductQueryServiceImpl implements ProductQueryService { BigDecimal price = bestOffer != null ? bestOffer.getEffectivePrice() : null; String buyUrl = bestOffer != null ? bestOffer.getBuyUrl() : null; + String buyShortUrl = bestOffer != null + ? shortLinkService.shortBuyUrlForOffer(bestOffer.getId(), buyUrl) + : null; - return ProductMapper.toSummary(p, price, buyUrl); + return ProductMapper.toSummary(p, price, buyUrl, buyShortUrl); }) .toList(); } @@ -89,9 +91,6 @@ public class ProductQueryServiceImpl implements ProductQueryService { ) { final boolean allPlatforms = platform != null && platform.equalsIgnoreCase("ALL"); - // IMPORTANT: ignore Pageable sorting (because we are doing our own "best price" logic) - // If the client accidentally passes ?sort=..., Spring Data will try ordering by a Product field. - // We'll strip it to be safe. Pageable safePageable = pageable; if (pageable != null && pageable.getSort() != null && pageable.getSort().isSorted()) { safePageable = Pageable.ofSize(pageable.getPageSize()).withPage(pageable.getPageNumber()); @@ -116,14 +115,12 @@ public class ProductQueryServiceImpl implements ProductQueryService { List productIds = products.stream().map(Product::getId).toList(); - // Only fetch offers for THIS PAGE of products List allOffers = productOfferRepository.findByProduct_IdIn(productIds); Map> offersByProductId = allOffers.stream() .filter(o -> o.getProduct() != null && o.getProduct().getId() != null) .collect(Collectors.groupingBy(o -> o.getProduct().getId())); - // Build DTOs (same as before) List dtos = products.stream() .map(p -> { List offersForProduct = @@ -133,12 +130,14 @@ public class ProductQueryServiceImpl implements ProductQueryService { BigDecimal price = bestOffer != null ? bestOffer.getEffectivePrice() : null; String buyUrl = bestOffer != null ? bestOffer.getBuyUrl() : null; + String buyShortUrl = bestOffer != null + ? shortLinkService.shortBuyUrlForOffer(bestOffer.getId(), buyUrl) + : null; - return ProductMapper.toSummary(p, price, buyUrl); + return ProductMapper.toSummary(p, price, buyUrl, buyShortUrl); }) .toList(); - // Phase 3 "server-side sort by price" (within the page for now) Comparator byPriceNullLast = Comparator.comparing(ProductSummaryDto::getPrice, Comparator.nullsLast(Comparator.naturalOrder())); @@ -151,13 +150,8 @@ public class ProductQueryServiceImpl implements ProductQueryService { return new PageImpl<>(dtos, safePageable, productPage.getTotalElements()); } - // - // Product Offers - // - @Override public List getOffersForProduct(Integer productId) { - // ✅ canonical repo method List offers = productOfferRepository.findByProduct_Id(productId); return offers.stream() @@ -180,20 +174,21 @@ public class ProductQueryServiceImpl implements ProductQueryService { Product product = productRepository.findById(productId).orElse(null); if (product == null) return null; - // ✅ canonical repo method List offers = productOfferRepository.findByProduct_Id(productId); ProductOffer bestOffer = pickBestOffer(offers); BigDecimal price = bestOffer != null ? bestOffer.getEffectivePrice() : null; String buyUrl = bestOffer != null ? bestOffer.getBuyUrl() : null; + String buyShortUrl = bestOffer != null + ? shortLinkService.shortBuyUrlForOffer(bestOffer.getId(), buyUrl) + : null; - return ProductMapper.toSummary(product, price, buyUrl); + return ProductMapper.toSummary(product, price, buyUrl, buyShortUrl); } private ProductOffer pickBestOffer(List offers) { if (offers == null || offers.isEmpty()) return null; - // MVP: lowest effective price wins. (Later: prefer in-stock, etc.) return offers.stream() .filter(o -> o.getEffectivePrice() != null) .min(Comparator.comparing(ProductOffer::getEffectivePrice)) diff --git a/src/main/java/group/goforward/battlbuilder/web/dto/ProductSummaryDto.java b/src/main/java/group/goforward/battlbuilder/web/dto/ProductSummaryDto.java index 5cbf0ba..aeb15ed 100644 --- a/src/main/java/group/goforward/battlbuilder/web/dto/ProductSummaryDto.java +++ b/src/main/java/group/goforward/battlbuilder/web/dto/ProductSummaryDto.java @@ -12,6 +12,7 @@ public class ProductSummaryDto { private String categoryKey; private BigDecimal price; private String buyUrl; + private String buyShortUrl; private String imageUrl; @@ -79,6 +80,9 @@ public class ProductSummaryDto { this.buyUrl = buyUrl; } + public String getBuyShortUrl() { return buyShortUrl; } + public void setBuyShortUrl(String buyShortUrl) { this.buyShortUrl = buyShortUrl; } + public String getImageUrl() { return imageUrl; } public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; } diff --git a/src/main/java/group/goforward/battlbuilder/web/mapper/ProductMapper.java b/src/main/java/group/goforward/battlbuilder/web/mapper/ProductMapper.java index 308b40a..ef630eb 100644 --- a/src/main/java/group/goforward/battlbuilder/web/mapper/ProductMapper.java +++ b/src/main/java/group/goforward/battlbuilder/web/mapper/ProductMapper.java @@ -7,10 +7,9 @@ import java.math.BigDecimal; public class ProductMapper { - public static ProductSummaryDto toSummary(Product product, BigDecimal price, String buyUrl) { + public static ProductSummaryDto toSummary(Product product, BigDecimal price, String buyUrl, String buyShortUrl) { ProductSummaryDto dto = new ProductSummaryDto(); - // Product ID -> String dto.setId(String.valueOf(product.getId())); dto.setName(product.getName()); @@ -18,17 +17,14 @@ public class ProductMapper { dto.setPlatform(product.getPlatform()); dto.setPartRole(product.getPartRole()); - // Use rawCategoryKey from the Product entity dto.setCategoryKey(product.getRawCategoryKey()); - // Price + buy URL from offers dto.setPrice(price); - dto.setBuyUrl(buyUrl); + dto.setBuyUrl(buyUrl); // keep raw + dto.setBuyShortUrl(buyShortUrl); // new - // product image dto.setImageUrl(product.getMainImageUrl()); - return dto; } } \ No newline at end of file diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml new file mode 100644 index 0000000..00ba35b --- /dev/null +++ b/src/main/resources/application-dev.yml @@ -0,0 +1,3 @@ +shortlinks: + enabled: true + publicBaseUrl: "http://localhost:8080" \ No newline at end of file diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml new file mode 100644 index 0000000..68aff4a --- /dev/null +++ b/src/main/resources/application-prod.yml @@ -0,0 +1,3 @@ +shortlinks: + enabled: true + publicBaseUrl: "https://battl.build" \ No newline at end of file