some fixes, added api key as param

This commit is contained in:
Jan 2025-11-05 14:33:57 +01:00
parent dfbc1e00bd
commit 3c19a9191b
10 changed files with 74 additions and 100 deletions

View file

@ -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<String> 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);
}

View file

@ -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<Measure> getMeasures() {
public Iterable<Measure> 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();
}

View file

@ -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<Nomenclature> 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<Nomenclature> 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);
}
}

View file

@ -55,9 +55,9 @@ public class TariffController {
@GetMapping("")
public Map<String, Map<String, List<AppliedMeasure>>> 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);
}
}

View file

@ -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;
}
}

View file

@ -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<Measure, Integer> {
@Query("SELECT * FROM measure m LEFT JOIN measure_series s ON m.measure_series_id = s.id WHERE s.measure_series_code = :series")
Iterable<Measure> findByMeasureSeries(String series);
}

View file

@ -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<Nomenclature, Integer> {
@Query("SELECT * FROM nomenclature WHERE hscode = :hscode AND (end_date IS NULL OR end_date >= CURRENT_DATE)")
Optional<Nomenclature> 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<Nomenclature> findDeclarableChildren(@Param("code") String code);
}

View file

@ -25,26 +25,32 @@ public class DescSetSerializer extends JsonSerializer<Set<Description>> {
@Override
public void serialize(Set<Description> 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<Description> 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();
}

View file

@ -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<Nomenclature> getDeclarableChildren(String hscode, Integer limit) {
// var normalized = normalize(hscode.replaceAll("[^0-9]", ""));
// var level = getHierarchyLevel(normalized);
public List<Nomenclature> 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<Nomenclature> getNomenclatureCascade(String hscode) {

View file

@ -63,23 +63,6 @@ public class TariffService {
}
private Optional<Double> findTariff(Collection<AppliedMeasure> measures, Measure appl) {
var code = appl.getMeasureCode();
//TODO return all measures...
List<AppliedMeasure> 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<AppliedMeasure> findAppliedMeasureByGeo(Nomenclature nomenclature, Geo geo) {
var foundImport = importRepository.findByNomenclatureAndGeo(nomenclature.getId(), geo.getId());