From 3c19a9191b2c3af9479c02fbbd80b95ff3a2ab9b Mon Sep 17 00:00:00 2001 From: Jan Date: Wed, 5 Nov 2025 14:33:57 +0100 Subject: [PATCH] some fixes, added api key as param --- .../de/avatic/taric/config/ApiKeyFilter.java | 27 ++++++++---- .../taric/controller/MeasureController.java | 11 +++-- .../controller/NomenclatureController.java | 10 +++-- .../taric/controller/TariffController.java | 4 +- .../avatic/taric/model/MonetaryUnitDesc.java | 43 ++----------------- .../taric/repository/MeasureRepository.java | 3 ++ .../repository/NomenclatureRepository.java | 5 +++ .../taric/serializer/DescSetSerializer.java | 40 +++++++++-------- .../taric/service/NomenclatureService.java | 14 +++--- .../avatic/taric/service/TariffService.java | 17 -------- 10 files changed, 74 insertions(+), 100 deletions(-) diff --git a/src/main/java/de/avatic/taric/config/ApiKeyFilter.java b/src/main/java/de/avatic/taric/config/ApiKeyFilter.java index 44abd75..dd75e6a 100644 --- a/src/main/java/de/avatic/taric/config/ApiKeyFilter.java +++ b/src/main/java/de/avatic/taric/config/ApiKeyFilter.java @@ -21,6 +21,7 @@ import java.util.List; public class ApiKeyFilter extends OncePerRequestFilter { private static final String API_KEY_HEADER = "X-API-Key"; + private static final String API_KEY_PARAM = "apiKey"; private static final List PUBLIC_PATHS = List.of( "/actuator/health", @@ -29,7 +30,7 @@ public class ApiKeyFilter extends OncePerRequestFilter { "/v3/api-docs" ); - @Value("${taric.api-key}") + @Value("${taric.api.key}") private String validApiKey; @Override @@ -39,7 +40,6 @@ public class ApiKeyFilter extends OncePerRequestFilter { String path = request.getRequestURI(); - if ("OPTIONS".equals(request.getMethod())) { filterChain.doFilter(request, response); return; @@ -50,15 +50,9 @@ public class ApiKeyFilter extends OncePerRequestFilter { return; } - String apiKey = request.getHeader(API_KEY_HEADER); - - if (apiKey == null) { - apiKey = request.getHeader(API_KEY_HEADER.toLowerCase()); - } + String apiKey = extractApiKey(request); if (validApiKey != null && validApiKey.equals(apiKey)) { - - UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( "api-user", @@ -76,6 +70,21 @@ public class ApiKeyFilter extends OncePerRequestFilter { } } + private String extractApiKey(HttpServletRequest request) { + + String apiKey = request.getHeader(API_KEY_HEADER); + + if (apiKey == null) { + apiKey = request.getHeader(API_KEY_HEADER.toLowerCase()); + } + + if (apiKey == null) { + apiKey = request.getParameter(API_KEY_PARAM); + } + + return apiKey; + } + private boolean isPublicPath(String path) { return PUBLIC_PATHS.stream().anyMatch(path::startsWith); } diff --git a/src/main/java/de/avatic/taric/controller/MeasureController.java b/src/main/java/de/avatic/taric/controller/MeasureController.java index 5a32cbd..09301d8 100644 --- a/src/main/java/de/avatic/taric/controller/MeasureController.java +++ b/src/main/java/de/avatic/taric/controller/MeasureController.java @@ -7,6 +7,7 @@ import de.avatic.taric.repository.MeasureActionRepository; import de.avatic.taric.repository.MeasureRepository; import de.avatic.taric.repository.MeasureSeriesRepository; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; @@ -14,10 +15,9 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import java.util.List; - @RestController @RequestMapping("/api/v1/measures") @Tag(name = "Measures", description = "API endpoints for trade measures, measure series, and measure actions") @@ -45,7 +45,12 @@ public class MeasureController { ) }) @GetMapping - public Iterable getMeasures() { + public Iterable getMeasures(@Parameter(description = "Optional series to filter measures by series") + @RequestParam(required = false) String series) { + + if( series != null ) + return measureRepository.findByMeasureSeries(series); + return measureRepository.findAll(); } diff --git a/src/main/java/de/avatic/taric/controller/NomenclatureController.java b/src/main/java/de/avatic/taric/controller/NomenclatureController.java index 0573522..a528de2 100644 --- a/src/main/java/de/avatic/taric/controller/NomenclatureController.java +++ b/src/main/java/de/avatic/taric/controller/NomenclatureController.java @@ -9,6 +9,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.constraints.Min; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -65,8 +66,10 @@ public class NomenclatureController { @GetMapping("/declarable") public List getDeclarable( @Parameter(description = "Parent HS code to retrieve declarable children from", required = true) - @RequestParam String hscode) { - return nomenclatureService.getDeclarableChildren(hscode); + @RequestParam String hscode, + @Parameter(description = "Limit (default =10)", required = true) + @RequestParam(required = false, defaultValue = "10") @Min(1) Integer limit) { + return nomenclatureService.getDeclarableChildren(hscode, limit); } @Operation( @@ -84,7 +87,6 @@ public class NomenclatureController { public List getCascade( @Parameter(description = "HS code to retrieve the complete hierarchy for", required = true) @RequestParam String hscode) { - var found = nomenclatureService.getNomenclatureCascade(hscode); - return found; + return nomenclatureService.getNomenclatureCascade(hscode); } } \ No newline at end of file diff --git a/src/main/java/de/avatic/taric/controller/TariffController.java b/src/main/java/de/avatic/taric/controller/TariffController.java index 1e0d511..9078dc6 100644 --- a/src/main/java/de/avatic/taric/controller/TariffController.java +++ b/src/main/java/de/avatic/taric/controller/TariffController.java @@ -55,9 +55,9 @@ public class TariffController { @GetMapping("") public Map>> getTariffRate( @Parameter(description = "Harmonized System code (e.g., 0101210000)", required = true, example = "0101210000") - @RequestParam String hsCode, + @RequestParam String hscode, @Parameter(description = "ISO country code (e.g., US, CN, GB)", required = true, example = "US") @RequestParam String countryCode) { - return tariffService.getAppliedMeasures(hsCode, countryCode); + return tariffService.getAppliedMeasures(hscode, countryCode); } } \ No newline at end of file diff --git a/src/main/java/de/avatic/taric/model/MonetaryUnitDesc.java b/src/main/java/de/avatic/taric/model/MonetaryUnitDesc.java index 67488a0..a8a5623 100644 --- a/src/main/java/de/avatic/taric/model/MonetaryUnitDesc.java +++ b/src/main/java/de/avatic/taric/model/MonetaryUnitDesc.java @@ -2,11 +2,15 @@ package de.avatic.taric.model; import java.time.LocalDate; +import lombok.Getter; +import lombok.Setter; import org.springframework.data.annotation.Id; import jakarta.validation.constraints.Size; +@Getter +@Setter public class MonetaryUnitDesc { @Id @@ -19,43 +23,4 @@ public class MonetaryUnitDesc { private LocalDate descStartDate; - public MonetaryUnitDesc(String lang, String desc, LocalDate descStartDate) { - this.lang = lang; - this.desc = desc; - this.descStartDate = descStartDate; - } - - public Integer getId() { - return id; - } - - public void setId(final Integer id) { - this.id = id; - } - - public String getLang() { - return lang; - } - - public void setLang(final String lang) { - this.lang = lang; - } - - public String getDesc() { - return desc; - } - - public void setDescc(final String desc) { - this.desc = desc; - } - - public LocalDate getDescStartDate() { - return descStartDate; - } - - public void setDescStartDate(final LocalDate descStartDate) { - this.descStartDate = descStartDate; - } - - } diff --git a/src/main/java/de/avatic/taric/repository/MeasureRepository.java b/src/main/java/de/avatic/taric/repository/MeasureRepository.java index 6ad45b9..cbd980c 100644 --- a/src/main/java/de/avatic/taric/repository/MeasureRepository.java +++ b/src/main/java/de/avatic/taric/repository/MeasureRepository.java @@ -1,5 +1,6 @@ package de.avatic.taric.repository; +import org.springframework.data.jdbc.repository.query.Query; import org.springframework.data.repository.CrudRepository; import de.avatic.taric.model.Measure; @@ -7,4 +8,6 @@ import de.avatic.taric.model.Measure; public interface MeasureRepository extends CrudRepository { + @Query("SELECT * FROM measure m LEFT JOIN measure_series s ON m.measure_series_id = s.id WHERE s.measure_series_code = :series") + Iterable findByMeasureSeries(String series); } diff --git a/src/main/java/de/avatic/taric/repository/NomenclatureRepository.java b/src/main/java/de/avatic/taric/repository/NomenclatureRepository.java index a275c68..acd5a41 100644 --- a/src/main/java/de/avatic/taric/repository/NomenclatureRepository.java +++ b/src/main/java/de/avatic/taric/repository/NomenclatureRepository.java @@ -1,20 +1,25 @@ package de.avatic.taric.repository; import de.avatic.taric.model.Nomenclature; +import org.springframework.data.domain.Limit; import org.springframework.data.jdbc.repository.query.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import java.time.LocalDate; import java.util.List; import java.util.Optional; @Repository public interface NomenclatureRepository extends CrudRepository { + @Query("SELECT * FROM nomenclature WHERE hscode = :hscode AND (end_date IS NULL OR end_date >= CURRENT_DATE)") Optional findByHscode(String hscode); @Query("SELECT * FROM nomenclature WHERE is_leaf = 1 AND hscode LIKE CONCAT(:code, '%') AND (end_date IS NULL OR end_date >= CURRENT_DATE)") List findDeclarableChildren(@Param("code") String code); + + } \ No newline at end of file diff --git a/src/main/java/de/avatic/taric/serializer/DescSetSerializer.java b/src/main/java/de/avatic/taric/serializer/DescSetSerializer.java index 1dc44ac..925877a 100644 --- a/src/main/java/de/avatic/taric/serializer/DescSetSerializer.java +++ b/src/main/java/de/avatic/taric/serializer/DescSetSerializer.java @@ -25,26 +25,32 @@ public class DescSetSerializer extends JsonSerializer> { @Override public void serialize(Set value, JsonGenerator gen, SerializerProvider serializer) throws IOException { - if (value != null) { - var filtered = value; + if (value == null || value.isEmpty()) { + gen.writeNull(); + return; + } - String language = LANGUAGE_CONTEXT.get(); - if (language != null) { - filtered = value.stream() - .filter(desc -> language.equalsIgnoreCase(desc.getLang())) - .collect(Collectors.toSet()); - } + String language = LANGUAGE_CONTEXT.get(); - if (filtered.isEmpty()) { - filtered = value.stream() - .filter(desc -> "EN".equalsIgnoreCase(desc.getLang())) - .collect(Collectors.toSet()); - } + Set filtered = Set.of(); + if (language != null) { + filtered = value.stream() + .filter(desc -> language.equalsIgnoreCase(desc.getLang())) + .collect(Collectors.toSet()); + } - if(filtered.size() == 1) - gen.writeObject(filtered.iterator().next()); - else - gen.writeObject(filtered); + if (filtered.isEmpty()) { + filtered = value.stream() + .filter(desc -> "EN".equalsIgnoreCase(desc.getLang())) + .collect(Collectors.toSet()); + } + + if (filtered.isEmpty() && !value.isEmpty()) { + filtered = Set.of(value.iterator().next()); + } + + if (!filtered.isEmpty()) { + gen.writeObject(filtered.iterator().next()); } else { gen.writeNull(); } diff --git a/src/main/java/de/avatic/taric/service/NomenclatureService.java b/src/main/java/de/avatic/taric/service/NomenclatureService.java index 03d10d0..8a4049a 100644 --- a/src/main/java/de/avatic/taric/service/NomenclatureService.java +++ b/src/main/java/de/avatic/taric/service/NomenclatureService.java @@ -2,6 +2,7 @@ package de.avatic.taric.service; import de.avatic.taric.model.Nomenclature; import de.avatic.taric.repository.NomenclatureRepository; +import org.springframework.data.domain.Limit; import org.springframework.stereotype.Service; import java.util.*; @@ -20,16 +21,11 @@ public class NomenclatureService { return nomenclatureRepository.findByHscode(normalize(hscode.replaceAll("[^0-9]", ""))); } - public boolean isDeclarable(String hscode) { - var nomenclature = getNomenclature(hscode); - if (nomenclature.isEmpty()) return false; - return nomenclature.get().getIsLeaf(); - } + public List getDeclarableChildren(String hscode, Integer limit) { +// var normalized = normalize(hscode.replaceAll("[^0-9]", "")); +// var level = getHierarchyLevel(normalized); - public List getDeclarableChildren(String hscode) { - var normalized = normalize(hscode.replaceAll("[^0-9]", "")); - var level = getHierarchyLevel(normalized); - return nomenclatureRepository.findDeclarableChildren(normalized.substring(0, level)); + return nomenclatureRepository.findDeclarableChildren(hscode).stream().limit(limit).toList(); } public List getNomenclatureCascade(String hscode) { diff --git a/src/main/java/de/avatic/taric/service/TariffService.java b/src/main/java/de/avatic/taric/service/TariffService.java index ea4e56d..58a3214 100644 --- a/src/main/java/de/avatic/taric/service/TariffService.java +++ b/src/main/java/de/avatic/taric/service/TariffService.java @@ -63,23 +63,6 @@ public class TariffService { } - private Optional findTariff(Collection measures, Measure appl) { - - var code = appl.getMeasureCode(); - - //TODO return all measures... - - List filteredMeasures = measures.stream() - .filter(meas -> meas.getAmount() != null && meas.getAmount().trim().matches("\\d+\\.\\d+\\s*%")) - .toList(); - - AppliedMeasure customTariff = filteredMeasures.stream().filter(meas -> meas.getMeasure().getId().equals(appl.getId())).findAny().orElse(filteredMeasures.isEmpty() ? null : filteredMeasures.getFirst()); - - if (!filteredMeasures.isEmpty()) - return Optional.of(customTariff).map(meas -> Double.parseDouble(meas.getAmount().trim().replace("%", "").trim()) / 100); - return Optional.empty(); - } - public List findAppliedMeasureByGeo(Nomenclature nomenclature, Geo geo) { var foundImport = importRepository.findByNomenclatureAndGeo(nomenclature.getId(), geo.getId());