From 68b4e453a952d1f49f8fabcad7219964359ef047 Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Mon, 1 Sep 2025 22:12:43 -0400 Subject: [PATCH 01/38] Plugin - Ext Item Search - Setup beginnings of new world order --- .../extItemSearch/model/ExtItemLookupResults.java | 12 ++++++++++++ .../oqm/plugin/extItemSearch/model/SearchType.java | 9 +++++++++ .../extItemSearchService/ExtItemSearchService.java | 10 ++++++++++ .../exceptions/SearchNotSupportedException.java | 10 ++++++++++ .../interfaces/BarcodeSearching.java | 5 +++++ .../interfaces/LegoSearching.java | 5 +++++ .../interfaces/TextSearching.java | 9 +++++++++ .../interfaces/UrlSearching.java | 8 ++++++++ 8 files changed, 68 insertions(+) create mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/SearchType.java create mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/ExtItemSearchService.java create mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/exceptions/SearchNotSupportedException.java create mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/BarcodeSearching.java create mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/LegoSearching.java create mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/TextSearching.java create mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/UrlSearching.java diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupResults.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupResults.java index 130991e14f..d5f5257e93 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupResults.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupResults.java @@ -26,4 +26,16 @@ public class ExtItemLookupResults { @NotNull @lombok.Builder.Default private Map<@NonNull @NotNull @NotBlank String, String> serviceErrs = new HashMap<>(); + + public ExtItemLookupResults combine(ExtItemLookupResults other) { + ExtItemLookupResults output = new ExtItemLookupResults(); + + output.getResults().addAll(this.getResults()); + output.getResults().addAll(other.getResults()); + + output.getServiceErrs().putAll(this.getServiceErrs()); + output.getServiceErrs().putAll(other.getServiceErrs()); + + return this; + } } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/SearchType.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/SearchType.java new file mode 100644 index 0000000000..545211d0b5 --- /dev/null +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/SearchType.java @@ -0,0 +1,9 @@ +package tech.ebp.oqm.plugin.extItemSearch.model; + + +public enum SearchType { + TEXT, + URL, + BARCODE, + LEGO_PART_NUM, +} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/ExtItemSearchService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/ExtItemSearchService.java new file mode 100644 index 0000000000..fb864b958e --- /dev/null +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/ExtItemSearchService.java @@ -0,0 +1,10 @@ +package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService; + +import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupResults; +import tech.ebp.oqm.plugin.extItemSearch.model.SearchType; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.exceptions.SearchNotSupportedException; + +public abstract class ExtItemSearchService { + + +} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/exceptions/SearchNotSupportedException.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/exceptions/SearchNotSupportedException.java new file mode 100644 index 0000000000..0491a8df7b --- /dev/null +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/exceptions/SearchNotSupportedException.java @@ -0,0 +1,10 @@ +package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.exceptions; + +import tech.ebp.oqm.plugin.extItemSearch.model.SearchType; + +public class SearchNotSupportedException extends Exception { + + public SearchNotSupportedException(SearchType type) { + super("Not supported: " + type); + } +} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/BarcodeSearching.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/BarcodeSearching.java new file mode 100644 index 0000000000..7457a64724 --- /dev/null +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/BarcodeSearching.java @@ -0,0 +1,5 @@ +package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.interfaces; + +public interface BarcodeSearching { + +} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/LegoSearching.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/LegoSearching.java new file mode 100644 index 0000000000..da4d0533dd --- /dev/null +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/LegoSearching.java @@ -0,0 +1,5 @@ +package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.interfaces; + +public interface LegoSearching { + +} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/TextSearching.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/TextSearching.java new file mode 100644 index 0000000000..5fa36564fd --- /dev/null +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/TextSearching.java @@ -0,0 +1,9 @@ +package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.interfaces; + +import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupResults; + +public interface TextSearching { + + ExtItemLookupResults searchText(String url) throws Exception; + +} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/UrlSearching.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/UrlSearching.java new file mode 100644 index 0000000000..8da3a7eefb --- /dev/null +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/UrlSearching.java @@ -0,0 +1,8 @@ +package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.interfaces; + +import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupResults; + +public interface UrlSearching { + + ExtItemLookupResults searchUrl(String url) throws Exception; +} From 0c02226d9a3b2b4b822913f0cc055bcac1662e3d Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Mon, 20 Apr 2026 23:43:34 -0400 Subject: [PATCH 02/38] Plugin - Ext Item Search - Next iteration on rework of search --- .../plugins/external-item-search/build.gradle | 5 +- .../dev/services/datakick/README.md | 3 + .../datakick}/datakick_barcode.json | 0 .../mappings/barcodelookup_barcode.json | 0 .../mappings/upcitemdb_barcode.json | 0 .../mappings/upcitemdb_trial_barcode.json | 0 .../dev/services/rebrickable/README.md | 4 + .../rebrickable}/rebrickable_part.json | 0 .../external-item-search/gradle.properties | 4 +- .../interfaces/ItemLookupRestInterface.java | 73 ++---- .../model/ExtItemLookupProviderInfo.java | 19 +- .../extItemSearch/model/ExtItemSearch.java | 40 ++++ .../lookupResult/ExtItemLookupErrResult.java | 36 +++ .../ExtItemLookupResult.java | 24 +- .../ExtItemLookupResults.java | 5 +- .../model/lookupResult/LookupResult.java | 31 +++ .../model/lookupResult/ResultType.java | 6 + .../service/ExtItemLookupService.java | 216 ++++++------------ .../ExtItemSearchService.java | 4 - .../interfaces/TextSearching.java | 2 +- .../interfaces/UrlSearching.java | 2 +- .../searchServices/ItemSearchService.java | 54 +++++ .../api/ItemApiSearchService.java | 2 +- .../api/lego/LegoLookupService.java | 25 -- .../api/product/ApiProductSearchService.java | 2 +- .../product/upcItemDb/UpcItemDbService.java | 2 +- .../barcodeLookup/BarcodeLookupClient.java | 2 +- .../barcodeLookup/BarcodeLookupService.java | 24 +- .../dataKick/DataKickLookupClient.java | 9 +- .../dataKick/DatakickService.java} | 100 ++++---- .../rebrickable/RebrickableLookupClient.java | 10 +- .../rebrickable/RebrickableService.java | 108 ++++----- .../searchServices/utils/ItemKind.java | 5 + .../searchServices/utils/LookupType.java | 9 + .../AdafruitWebProductScrapeService.java | 2 +- .../AmazonWebProductScrapeService.java | 2 +- .../GenericWebProductScrapeService.java | 2 +- .../webPage/WebPageProductScrapeService.java | 2 +- .../src/main/resources/application.yml | 28 +-- 39 files changed, 458 insertions(+), 404 deletions(-) create mode 100644 software/plugins/external-item-search/dev/services/datakick/README.md rename software/plugins/external-item-search/dev/{wmMapping/mappings => services/datakick}/datakick_barcode.json (100%) rename software/plugins/external-item-search/dev/{wmMapping => services}/mappings/barcodelookup_barcode.json (100%) rename software/plugins/external-item-search/dev/{wmMapping => services}/mappings/upcitemdb_barcode.json (100%) rename software/plugins/external-item-search/dev/{wmMapping => services}/mappings/upcitemdb_trial_barcode.json (100%) create mode 100644 software/plugins/external-item-search/dev/services/rebrickable/README.md rename software/plugins/external-item-search/dev/{wmMapping/mappings => services/rebrickable}/rebrickable_part.json (100%) create mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemSearch.java create mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupErrResult.java rename software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/{ => lookupResult}/ExtItemLookupResult.java (77%) rename software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/{ => lookupResult}/ExtItemLookupResults.java (93%) create mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/LookupResult.java create mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ResultType.java delete mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/lego/LegoLookupService.java rename software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/{api/product => providers}/barcodeLookup/BarcodeLookupClient.java (95%) rename software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/{api/product => providers}/barcodeLookup/BarcodeLookupService.java (87%) rename software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/{api/product => providers}/dataKick/DataKickLookupClient.java (66%) rename software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/{api/product/dataKick/DataKickService.java => providers/dataKick/DatakickService.java} (51%) rename software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/{api/lego => providers}/rebrickable/RebrickableLookupClient.java (72%) rename software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/{api/lego => providers}/rebrickable/RebrickableService.java (57%) create mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/ItemKind.java create mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupType.java diff --git a/software/plugins/external-item-search/build.gradle b/software/plugins/external-item-search/build.gradle index 21aca7ddf8..68adfc861b 100644 --- a/software/plugins/external-item-search/build.gradle +++ b/software/plugins/external-item-search/build.gradle @@ -26,6 +26,7 @@ dependencies { implementation 'io.quarkus:quarkus-micrometer' implementation 'io.quarkus:quarkus-hibernate-validator' implementation 'io.quarkus:quarkus-container-image-docker' + implementation 'io.quarkus:quarkus-cache' implementation 'io.quarkus:quarkus-arc' implementation group: 'org.jsoup', name: 'jsoup', version: '1.22.1' @@ -38,8 +39,8 @@ dependencies { } java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 } test { diff --git a/software/plugins/external-item-search/dev/services/datakick/README.md b/software/plugins/external-item-search/dev/services/datakick/README.md new file mode 100644 index 0000000000..d5bf6c7b24 --- /dev/null +++ b/software/plugins/external-item-search/dev/services/datakick/README.md @@ -0,0 +1,3 @@ +# Datakick Service + + - https://gtinsearch.org/api \ No newline at end of file diff --git a/software/plugins/external-item-search/dev/wmMapping/mappings/datakick_barcode.json b/software/plugins/external-item-search/dev/services/datakick/datakick_barcode.json similarity index 100% rename from software/plugins/external-item-search/dev/wmMapping/mappings/datakick_barcode.json rename to software/plugins/external-item-search/dev/services/datakick/datakick_barcode.json diff --git a/software/plugins/external-item-search/dev/wmMapping/mappings/barcodelookup_barcode.json b/software/plugins/external-item-search/dev/services/mappings/barcodelookup_barcode.json similarity index 100% rename from software/plugins/external-item-search/dev/wmMapping/mappings/barcodelookup_barcode.json rename to software/plugins/external-item-search/dev/services/mappings/barcodelookup_barcode.json diff --git a/software/plugins/external-item-search/dev/wmMapping/mappings/upcitemdb_barcode.json b/software/plugins/external-item-search/dev/services/mappings/upcitemdb_barcode.json similarity index 100% rename from software/plugins/external-item-search/dev/wmMapping/mappings/upcitemdb_barcode.json rename to software/plugins/external-item-search/dev/services/mappings/upcitemdb_barcode.json diff --git a/software/plugins/external-item-search/dev/wmMapping/mappings/upcitemdb_trial_barcode.json b/software/plugins/external-item-search/dev/services/mappings/upcitemdb_trial_barcode.json similarity index 100% rename from software/plugins/external-item-search/dev/wmMapping/mappings/upcitemdb_trial_barcode.json rename to software/plugins/external-item-search/dev/services/mappings/upcitemdb_trial_barcode.json diff --git a/software/plugins/external-item-search/dev/services/rebrickable/README.md b/software/plugins/external-item-search/dev/services/rebrickable/README.md new file mode 100644 index 0000000000..441700e712 --- /dev/null +++ b/software/plugins/external-item-search/dev/services/rebrickable/README.md @@ -0,0 +1,4 @@ +# Rebrickable API + + - https://rebrickable.com/api/v3/docs/ + diff --git a/software/plugins/external-item-search/dev/wmMapping/mappings/rebrickable_part.json b/software/plugins/external-item-search/dev/services/rebrickable/rebrickable_part.json similarity index 100% rename from software/plugins/external-item-search/dev/wmMapping/mappings/rebrickable_part.json rename to software/plugins/external-item-search/dev/services/rebrickable/rebrickable_part.json diff --git a/software/plugins/external-item-search/gradle.properties b/software/plugins/external-item-search/gradle.properties index 3b7495c3d2..0bd2225299 100644 --- a/software/plugins/external-item-search/gradle.properties +++ b/software/plugins/external-item-search/gradle.properties @@ -1,6 +1,6 @@ #Gradle properties quarkusPluginId=io.quarkus -quarkusPluginVersion=3.32.4 -quarkusPlatformVersion=3.32.4 +quarkusPluginVersion=3.34.2 +quarkusPlatformVersion=3.34.2 quarkusPlatformGroupId=io.quarkus.platform quarkusPlatformArtifactId=quarkus-bom \ No newline at end of file diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java index e358d8b696..7049843832 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java @@ -1,8 +1,10 @@ package tech.ebp.oqm.plugin.extItemSearch.interfaces; +import io.smallrye.mutiny.Multi; import jakarta.annotation.security.PermitAll; import jakarta.enterprise.context.RequestScoped; import jakarta.inject.Inject; +import jakarta.ws.rs.BeanParam; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; @@ -18,7 +20,9 @@ import org.eclipse.microprofile.openapi.annotations.tags.Tag; import org.eclipse.microprofile.openapi.annotations.tags.Tags; import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; -import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupResults; +import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemSearch; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResults; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; import tech.ebp.oqm.plugin.extItemSearch.service.ExtItemLookupService; import java.net.MalformedURLException; @@ -33,7 +37,7 @@ public class ItemLookupRestInterface { @Inject ExtItemLookupService productLookupService; - + @GET @Path("/providers") @Operation( @@ -53,76 +57,29 @@ public class ItemLookupRestInterface { @PermitAll @Produces(MediaType.APPLICATION_JSON) public Response allProviderInfo() { - return Response.ok(this.productLookupService.getAllProviderInfo()).build(); - } - - @GET - @Path("barcode/{barcode}") - @Operation( - summary = "Searches enabled providers for the barcode given." - ) - @APIResponse( - responseCode = "200", - description = "Image retrieved.", - content = @Content( - mediaType = "application/json", - schema = @Schema( - implementation = ExtItemLookupResults.class - ) - ) - ) - @PermitAll - @Produces(MediaType.APPLICATION_JSON) - public Response searchBarcode( - @PathParam("barcode") String barcode - ) { - return Response.ok(this.productLookupService.searchBarcode(barcode)).build(); + return Response.ok(this.productLookupService.getProductProviderInfo()).build(); } - + @GET - @Path("webpage-scrape/{webpage}") + @Path("/search") @Operation( - summary = "Scans the given webpage for product details." + summary = "Searches." ) @APIResponse( responseCode = "200", description = "Image retrieved.", content = @Content( - mediaType = "application/json", - schema = @Schema( - implementation = ExtItemLookupResults.class - ) - ) - ) - @PermitAll - @Produces(MediaType.APPLICATION_JSON) - public Response scanWebpage( - @PathParam("webpage") String page - ) throws MalformedURLException, ExecutionException, InterruptedException { - return Response.ok(this.productLookupService.scanPage(new URL(page))).build(); - } - - @GET - @Path("lego/part/{partNo}") - @Operation( - summary = "Searches enabled providers for the lego part number." - ) - @APIResponse( - responseCode = "200", - description = "Image retrieved.", - content = @Content( - mediaType = "application/json", + mediaType = MediaType.APPLICATION_JSON, schema = @Schema( - implementation = ExtItemLookupResults.class + type = SchemaType.ARRAY, + implementation = ExtItemLookupProviderInfo.class ) ) ) @PermitAll @Produces(MediaType.APPLICATION_JSON) - public Response searchLegoPart( - @PathParam("partNo") String partNo - ) { - return Response.ok(this.productLookupService.searchLegoPart(partNo)).build(); + public Multi allProviderInfo(@BeanParam ExtItemSearch search) { + return this.productLookupService.search(search); } } \ No newline at end of file diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupProviderInfo.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupProviderInfo.java index 40efabd7fd..1cb7d31aa5 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupProviderInfo.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupProviderInfo.java @@ -2,8 +2,11 @@ import jakarta.validation.constraints.NotNull; import lombok.*; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.ItemKind; +import java.net.URI; import java.net.URL; +import java.util.List; /** * Information about an external item lookup provider @@ -14,6 +17,10 @@ @Builder public class ExtItemLookupProviderInfo implements Comparable { + @NotNull + @NonNull + private String id; + @NotNull @NonNull private String displayName; @@ -32,7 +39,17 @@ public class ExtItemLookupProviderInfo implements Comparable brands = List.of(); + + @NotNull + @NonNull + @lombok.Builder.Default + private List kinds = List.of(); private boolean enabled; diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemSearch.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemSearch.java new file mode 100644 index 0000000000..f5368ccd0d --- /dev/null +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemSearch.java @@ -0,0 +1,40 @@ +package tech.ebp.oqm.plugin.extItemSearch.model; + + +import jakarta.ws.rs.QueryParam; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.ItemKind; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupType; + +import java.util.List; + +@ToString +@Getter +@Setter +@Builder +public class ExtItemSearch { + + @Parameter(description = "The kind of item to search for. If empty, will search for any item. If multiple, treated as an 'or'.") + @QueryParam("kind") + List itemKinds; + + @Parameter(description = "The brand of item to search for. If empty, will search for any item. If multiple, treated as an 'or'.") + @QueryParam("brand") + List itemBrands; + + @Parameter(description = "The type of lookup to perform. If empty, will perform a free text search.") + @QueryParam("lookupType") + List lookupTypes; + + @Parameter(description = "The service(s) to use to search. If empty, will search all available services.") + @QueryParam("service") + List services; + + + @QueryParam("q") + String search; +} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupErrResult.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupErrResult.java new file mode 100644 index 0000000000..64c051755d --- /dev/null +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupErrResult.java @@ -0,0 +1,36 @@ +package tech.ebp.oqm.plugin.extItemSearch.model.lookupResult; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * An individual result from external sources. + */ +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Data +@AllArgsConstructor +@NoArgsConstructor +@SuperBuilder +public class ExtItemLookupErrResult extends LookupResult { + + @Override + public ResultType getType() { + return ResultType.ERROR; + } + + private String errMessage; +} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupResult.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupResult.java similarity index 77% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupResult.java rename to software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupResult.java index 9769a6ad13..9b6f340031 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupResult.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupResult.java @@ -1,11 +1,13 @@ -package tech.ebp.oqm.plugin.extItemSearch.model; +package tech.ebp.oqm.plugin.extItemSearch.model.lookupResult; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.NonNull; +import lombok.ToString; import lombok.experimental.SuperBuilder; import java.math.BigDecimal; @@ -17,19 +19,23 @@ /** * An individual result from external sources. */ +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) @Data @AllArgsConstructor @NoArgsConstructor @SuperBuilder -public class ExtItemLookupResult { - @NonNull - @NotNull - @NotBlank - private String source; +public class ExtItemLookupResult extends LookupResult { + + @Override + public ResultType getType() { + return ResultType.SUCCESS; + } private String brand; private String name; + @NonNull @NotNull @NotBlank @@ -54,11 +60,11 @@ public class ExtItemLookupResult { @lombok.Builder.Default private List<@NotNull @NonNull @NotBlank String> images = new ArrayList<>(); - public ExtItemLookupResult addAttIfNotBlank(String key, String val){ - if( + public ExtItemLookupResult addAttIfNotBlank(String key, String val) { + if ( key != null && !key.isBlank() && val != null && !val.isBlank() - ){ + ) { this.getAttributes().put(key, val); } return this; diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupResults.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupResults.java similarity index 93% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupResults.java rename to software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupResults.java index d5f5257e93..43f2846c25 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupResults.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupResults.java @@ -1,4 +1,4 @@ -package tech.ebp.oqm.plugin.extItemSearch.model; +package tech.ebp.oqm.plugin.extItemSearch.model.lookupResult; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; @@ -13,15 +13,18 @@ import java.util.List; import java.util.Map; +@Deprecated @Data @AllArgsConstructor @NoArgsConstructor @SuperBuilder public class ExtItemLookupResults { + @NonNull @NotNull @lombok.Builder.Default private List results = new ArrayList<>(); + @NonNull @NotNull @lombok.Builder.Default diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/LookupResult.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/LookupResult.java new file mode 100644 index 0000000000..65f638ce99 --- /dev/null +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/LookupResult.java @@ -0,0 +1,31 @@ +package tech.ebp.oqm.plugin.extItemSearch.model.lookupResult; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.experimental.SuperBuilder; + +@AllArgsConstructor +@NoArgsConstructor +@SuperBuilder +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type" +) +@JsonSubTypes({ + @JsonSubTypes.Type(value = ExtItemLookupResult.class, name = "SUCCESS"), + @JsonSubTypes.Type(value = ExtItemLookupErrResult.class, name = "ERROR") +}) +public abstract class LookupResult { + + public abstract ResultType getType(); + + @NonNull + @NotNull + @NotBlank + private String source; +} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ResultType.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ResultType.java new file mode 100644 index 0000000000..e3b0eaf585 --- /dev/null +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ResultType.java @@ -0,0 +1,6 @@ +package tech.ebp.oqm.plugin.extItemSearch.model.lookupResult; + +public enum ResultType { + SUCCESS, + ERROR +} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/ExtItemLookupService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/ExtItemLookupService.java index 82993e61ae..02108d99c6 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/ExtItemLookupService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/ExtItemLookupService.java @@ -1,201 +1,115 @@ package tech.ebp.oqm.plugin.extItemSearch.service; import io.opentelemetry.instrumentation.annotations.WithSpan; +import io.smallrye.mutiny.Multi; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; +import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemSearch; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.lego.LegoLookupService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.lego.rebrickable.RebrickableService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.product.ApiProductSearchService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.product.barcodeLookup.BarcodeLookupService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.product.dataKick.DataKickService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.product.upcItemDb.UpcItemDbService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.webPage.AdafruitWebProductScrapeService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.webPage.AmazonWebProductScrapeService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.webPage.WebPageProductScrapeService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.dataKick.DatakickService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.rebrickable.RebrickableService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.ItemKind; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupType; import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; -import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupResult; -import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupResults; -import java.net.URL; import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; -import java.util.concurrent.ExecutionException; +import java.util.stream.Stream; @ApplicationScoped @Slf4j @NoArgsConstructor public class ExtItemLookupService { - private static > List servicesToInfoList(T services){ + private static > List servicesToInfoList(T services) { List output = new ArrayList<>(services.size()); - - for (ItemSearchService curService :services) { + + for (ItemSearchService curService : services) { output.add(curService.getProviderInfo()); } output.sort(ExtItemLookupProviderInfo.Comparator.INSTANCE); return output; } - private final Set pageProductSearchServices = new HashSet<>(); - private final Set productSearchServices = new HashSet<>(); - private final Set legoSearchServices = new HashSet<>(); + Set searchServices = new HashSet<>(); @Inject public ExtItemLookupService( - AdafruitWebProductScrapeService adafruitWebProductScrapeService, - AmazonWebProductScrapeService amazonWebProductScrapeService, - DataKickService dataKickService, - BarcodeLookupService barcodeLookupService, - UpcItemDbService upcItemDbService, + DatakickService datakickService, RebrickableService rebrickableService ) { - this.pageProductSearchServices.add(adafruitWebProductScrapeService); - this.pageProductSearchServices.add(amazonWebProductScrapeService); - - this.productSearchServices.add(dataKickService); - this.productSearchServices.add(barcodeLookupService); - this.productSearchServices.add(upcItemDbService); - - this.legoSearchServices.add(rebrickableService); + this.searchServices.add(datakickService); + this.searchServices.add(rebrickableService); } - private ExtItemLookupResults processRequestsList(Map>> requests) { - List resultList = new ArrayList<>(requests.size()); - Map errList = new HashMap<>(); - - for (Map.Entry>> curRequest : requests.entrySet()) { - String curService = curRequest.getKey(); - CompletableFuture> curFuture = curRequest.getValue(); - List results; - - try { - results = curFuture.join(); - } catch(CompletionException e) { - log.error("FAILED to call {} service- ", curService, e); - errList.put(curService, e.getCause().getMessage()); - continue; - } - - resultList.addAll(results); - log.info("Got {} results from {}", results.size(), curService); - } - - - return ExtItemLookupResults.builder() - .results(resultList) - .serviceErrs(errList) - .build(); + public List getProductProviderInfo() { + return servicesToInfoList(this.searchServices); } - private ExtItemLookupResults processRequestsSingle(Map> requests) { - List resultList = new ArrayList<>(requests.size()); - Map errList = new HashMap<>(); + public List searchServicesMatching(ExtItemSearch search) { + Stream pending = this.searchServices.stream(); - for (Map.Entry> curRequest : requests.entrySet()) { - String curService = curRequest.getKey(); - CompletableFuture curFuture = curRequest.getValue(); - ExtItemLookupResult results; - - try { - results = curFuture.join(); - } catch(CompletionException e) { - log.error("FAILED to call {} service- ", curService, e); - errList.put(curService, e.getCause().getMessage()); - continue; - } - - resultList.add(results); - log.info("Got a result from {}", curService); + if (search.getServices() != null && !search.getServices().isEmpty()) { + pending = pending.filter(curService-> + search.getServices().contains(curService.getProviderInfo().getId()) + ); } - - return ExtItemLookupResults.builder() - .results(resultList) - .serviceErrs(errList) - .build(); - } - - public Map> getAllProviderInfo(){ - return Map.of( - "product", this.getProductProviderInfo(), - "lego", this.getLegoProviderInfo(), - "webpage", this.getSupportedPageScanInfo() - ); - } - - @WithSpan - public ExtItemLookupResults searchBarcode(String barcode) { - Map>> resultMap = new HashMap<>(); - - for (ApiProductSearchService curService : this.productSearchServices) { - Optional>> result = curService.searchBarcode(barcode); - - if (result.isPresent()) { - resultMap.put(curService.getProviderInfo().getDisplayName(), result.get()); - } + if (search.getItemKinds() != null && !search.getItemKinds().isEmpty()) { + pending = pending.filter(curService->{ + Collection supportedKinds = curService.getProviderInfo().getKinds(); + + if (supportedKinds.isEmpty()) { + return true; + } + + return search.getItemKinds().stream().anyMatch(supportedKinds::contains); + } + ); } - - return this.processRequestsList(resultMap); - } - - public ExtItemLookupResults searchProduct(String brand, String product) { - //TODO - return null; - } - - public List getProductProviderInfo() { - return servicesToInfoList(this.productSearchServices); - } - - @WithSpan - public ExtItemLookupResults searchLegoPart(String legoPartNum) { - Map>> resultMap = new HashMap<>(); - - for (LegoLookupService curService : this.legoSearchServices) { - Optional>> result = curService.searchPartNumber(legoPartNum); - - if (result.isPresent()) { - resultMap.put(curService.getProviderInfo().getDisplayName(), result.get()); - } + if (search.getItemBrands() != null && !search.getItemBrands().isEmpty()) { + pending = pending.filter(curService->{ + Collection supportedBrands = curService.getProviderInfo().getBrands(); + + if (supportedBrands.isEmpty()) { + return true; + } + + return search.getItemBrands().stream().anyMatch(supportedBrands::contains); + } + ); } - return this.processRequestsList(resultMap); + return pending.toList(); } - public List getLegoProviderInfo() { - return servicesToInfoList(this.legoSearchServices); - } - /** - * TODO:: many sites behind a wall checking for robots... - * @param page - * @return - * @throws ExecutionException - * @throws InterruptedException - */ - @WithSpan - public ExtItemLookupResults scanPage(URL page) throws ExecutionException, InterruptedException { - Map> resultMap = new HashMap<>(); + public Multi search(ExtItemSearch search) { - for (WebPageProductScrapeService curService : this.pageProductSearchServices) { - if(!curService.canParsePage(page)){ + List>> resultUnis = new ArrayList<>(this.searchServices.size()); + + for (ItemSearchService curService : this.searchServicesMatching(search)) { + if (search.getLookupTypes() == null || search.getLookupTypes().isEmpty()) { + resultUnis.add(curService.search(LookupType.FREE_TEXT, search.getSearch())); continue; } - Optional> result = curService.scrapeWebPage(page); - if (result.isPresent()) { - resultMap.put(curService.getProviderInfo().getDisplayName(), result.get()); + for (LookupType curType : search.getLookupTypes()) { + resultUnis.add( + curService.search( + curType, + search.getSearch() + ) + ); } } - return this.processRequestsSingle(resultMap); - } - - public List getSupportedPageScanInfo() { - return servicesToInfoList(this.pageProductSearchServices); + return Multi.createBy().merging().streams( + resultUnis.stream() + .filter(Optional::isPresent) + .map(Optional::get) + .toList() + ); } - } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/ExtItemSearchService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/ExtItemSearchService.java index fb864b958e..e9909fd7cb 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/ExtItemSearchService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/ExtItemSearchService.java @@ -1,9 +1,5 @@ package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService; -import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupResults; -import tech.ebp.oqm.plugin.extItemSearch.model.SearchType; -import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.exceptions.SearchNotSupportedException; - public abstract class ExtItemSearchService { diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/TextSearching.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/TextSearching.java index 5fa36564fd..196f30b38c 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/TextSearching.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/TextSearching.java @@ -1,6 +1,6 @@ package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.interfaces; -import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupResults; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResults; public interface TextSearching { diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/UrlSearching.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/UrlSearching.java index 8da3a7eefb..7fbdc16780 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/UrlSearching.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/UrlSearching.java @@ -1,6 +1,6 @@ package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.interfaces; -import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupResults; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResults; public interface UrlSearching { diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/ItemSearchService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/ItemSearchService.java index 64a7be978e..10e4adeb9e 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/ItemSearchService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/ItemSearchService.java @@ -1,11 +1,65 @@ package tech.ebp.oqm.plugin.extItemSearch.service.searchServices; +import io.smallrye.mutiny.Multi; import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupType; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; public abstract class ItemSearchService { public abstract ExtItemLookupProviderInfo getProviderInfo(); public abstract boolean isEnabled(); + + public Optional> searchName(String search) { + return Optional.empty(); + } + + public Optional> searchUrl(String search) { + return Optional.empty(); + } + + public Optional> searchBarcode(String search) { + return Optional.empty(); + } + + public Optional> searchPartNum(String search) { + return Optional.empty(); + } + + public Multi freeSearch(String search) { + List>> resultUniOps = new ArrayList<>(LookupType.values().length); + + for (LookupType type : LookupType.values()) { + switch (type) { + case NAME -> resultUniOps.add(this.searchName(search)); + case BARCODE -> resultUniOps.add(this.searchBarcode(search)); + case PART_NUM -> resultUniOps.add(this.searchPartNum(search)); + case URL -> resultUniOps.add(this.searchUrl(search)); + } + } + + List> resultUnis = resultUniOps.stream() + .filter(Optional::isPresent) + .map(Optional::get) + .toList(); + + return Multi.createBy().merging().streams(resultUnis); + } + + public Optional> search(LookupType type, String search) { + return switch (type) { + case FREE_TEXT -> Optional.of(this.freeSearch(search)); + case URL -> this.searchUrl(search); + case NAME -> this.searchName(search); + case BARCODE -> this.searchBarcode(search); + case PART_NUM -> this.searchPartNum(search); + }; + } + } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/ItemApiSearchService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/ItemApiSearchService.java index 5333471f97..020a8e1f30 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/ItemApiSearchService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/ItemApiSearchService.java @@ -1,7 +1,7 @@ package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api; import com.fasterxml.jackson.databind.JsonNode; -import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupResult; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResult; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; import java.util.List; diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/lego/LegoLookupService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/lego/LegoLookupService.java deleted file mode 100644 index 792f33b196..0000000000 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/lego/LegoLookupService.java +++ /dev/null @@ -1,25 +0,0 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.lego; - -import com.fasterxml.jackson.databind.JsonNode; -import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupResult; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.ItemApiSearchService; - -import java.util.List; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; - -public abstract class LegoLookupService extends ItemApiSearchService { - - protected abstract CompletionStage performPartNumberSearchCall(String barcode); - - public Optional>> searchPartNumber(String barcode){ - if(!this.isEnabled()){ - return Optional.empty(); - } - CompletionStage stage = this.performPartNumberSearchCall(barcode); - - return Optional.of(stage.thenApply(this::jsonNodeToSearchResults).toCompletableFuture()); - } - -} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/ApiProductSearchService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/ApiProductSearchService.java index bbdd0e479c..ef610a2835 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/ApiProductSearchService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/ApiProductSearchService.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.databind.JsonNode; import io.opentelemetry.instrumentation.annotations.WithSpan; import lombok.extern.slf4j.Slf4j; -import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupResult; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResult; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.ItemApiSearchService; import java.util.List; diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/upcItemDb/UpcItemDbService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/upcItemDb/UpcItemDbService.java index 79ec8779e0..8eb97a9ba3 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/upcItemDb/UpcItemDbService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/upcItemDb/UpcItemDbService.java @@ -12,7 +12,7 @@ import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.rest.client.inject.RestClient; import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; -import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupResult; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResult; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.product.ApiProductSearchService; import java.net.URL; diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/barcodeLookup/BarcodeLookupClient.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupClient.java similarity index 95% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/barcodeLookup/BarcodeLookupClient.java rename to software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupClient.java index c7f92320d9..024df81f5a 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/barcodeLookup/BarcodeLookupClient.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupClient.java @@ -1,4 +1,4 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.product.barcodeLookup; +package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.barcodeLookup; import com.fasterxml.jackson.databind.JsonNode; import io.opentelemetry.instrumentation.annotations.WithSpan; diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/barcodeLookup/BarcodeLookupService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupService.java similarity index 87% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/barcodeLookup/BarcodeLookupService.java rename to software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupService.java index 4f37b924d4..b878ddd967 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/barcodeLookup/BarcodeLookupService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupService.java @@ -1,4 +1,4 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.product.barcodeLookup; +package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.barcodeLookup; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; @@ -12,7 +12,8 @@ import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.rest.client.inject.RestClient; import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; -import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupResult; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResult; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.product.ApiProductSearchService; import java.net.URL; @@ -26,7 +27,7 @@ @ApplicationScoped @Slf4j @NoArgsConstructor -public class BarcodeLookupService extends ApiProductSearchService { +public class BarcodeLookupService extends ItemSearchService { BarcodeLookupClient barcodeLookupClient; @Getter @@ -55,14 +56,14 @@ public BarcodeLookupService( this.barcodeLookupClient = barcodeLookupClient; ExtItemLookupProviderInfo.Builder infoBuilder = ExtItemLookupProviderInfo - .builder() - .displayName(displayName) - .description(description) - .acceptsContributions(acceptsContributions) - .homepage(homepage) - .cost(cost); + .builder() + .displayName(displayName) + .description(description) + .acceptsContributions(acceptsContributions) + .homepage(homepage) + .cost(cost); - if(apiKey == null || apiKey.isBlank()){ + if (apiKey == null || apiKey.isBlank()) { log.warn("API key for {} was null or blank.", displayName); infoBuilder.enabled(false); this.apiKey = null; @@ -83,6 +84,7 @@ public boolean isEnabled() { * https://www.barcodelookup.com/api * * @param results + * * @return */ @WithSpan @@ -102,7 +104,7 @@ public List jsonNodeToSearchResults(JsonNode results) { for (Iterator> iter = curResultJson.fields(); iter.hasNext(); ) { Map.Entry curField = iter.next(); - if(curField.getValue().isObject() || curField.getValue().isArray()){ + if (curField.getValue().isObject() || curField.getValue().isArray()) { //TODO:: handle? continue; } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/dataKick/DataKickLookupClient.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DataKickLookupClient.java similarity index 66% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/dataKick/DataKickLookupClient.java rename to software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DataKickLookupClient.java index 54f6b1f1d8..3d8f6f7493 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/dataKick/DataKickLookupClient.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DataKickLookupClient.java @@ -1,7 +1,11 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.product.dataKick; +package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.dataKick; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import io.opentelemetry.instrumentation.annotations.WithSpan; +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; @@ -12,8 +16,9 @@ @Path("/api/items/") @RegisterRestClient(configKey = "upc-datakick") public interface DataKickLookupClient { + @WithSpan @GET @Path("{upc}") - CompletionStage getFromUpcCode(@PathParam("upc") String barcode); + Uni getFromUpcCode(@PathParam("upc") String barcode); } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/dataKick/DataKickService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java similarity index 51% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/dataKick/DataKickService.java rename to software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java index 79575fce26..2d8464cec7 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/dataKick/DataKickService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java @@ -1,9 +1,12 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.product.dataKick; +package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.dataKick; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import io.opentelemetry.instrumentation.annotations.WithSpan; +import io.quarkus.cache.CacheResult; +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import lombok.Getter; @@ -12,20 +15,20 @@ import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.rest.client.inject.RestClient; import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; -import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupResult; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.product.ApiProductSearchService; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResult; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; -import java.net.URL; +import java.net.URI; import java.util.*; -import java.util.concurrent.CompletionStage; /** - * - https://gtinsearch.org/ + * - https://gtinsearch.org/ */ @ApplicationScoped @Slf4j @NoArgsConstructor -public class DataKickService extends ApiProductSearchService { +public class DatakickService extends ItemSearchService { @Inject @RestClient @@ -34,68 +37,57 @@ public class DataKickService extends ApiProductSearchService { ExtItemLookupProviderInfo providerInfo; @Inject - public DataKickService( + public DatakickService( @RestClient DataKickLookupClient dataKickLookupClient, - @ConfigProperty(name = "productLookup.providers.datakick.displayName") - String displayName, @ConfigProperty(name = "productLookup.providers.datakick.enabled", defaultValue = "false") - boolean enabled, - @ConfigProperty(name = "productLookup.providers.datakick.description", defaultValue = "") - String description, - @ConfigProperty(name = "productLookup.providers.datakick.acceptsContributions", defaultValue = "") - boolean acceptsContributions, - @ConfigProperty(name = "productLookup.providers.datakick.homepage", defaultValue = "") - URL homepage, - @ConfigProperty(name = "productLookup.providers.datakick.cost", defaultValue = "") - String cost + boolean enabled ) { this.dataKickLookupClient = dataKickLookupClient; this.providerInfo = ExtItemLookupProviderInfo .builder() - .displayName(displayName) + .id("datakick") + .displayName("Datakick") .enabled(enabled) - .description(description) - .acceptsContributions(acceptsContributions) - .homepage(homepage) - .cost(cost) + .description( + "The open product database, free and open database of products. Mostly for home and food goods. Limited size of database, but free and open to contributions.") + .acceptsContributions(true) + .homepage(URI.create("https://gtinsearch.org/")) + .cost("Free") .build(); } + @CacheResult(cacheName = "rebrickable-part-num-search") + public Uni performBarcodeSearch(String barcode) { + return this.dataKickLookupClient.getFromUpcCode(barcode); + } + @Override public boolean isEnabled() { return this.getProviderInfo().isEnabled(); } /** - * https://gtinsearch.org/api - * https://www.gtinsearch.org/api/items/0754523765792 + * https://gtinsearch.org/api https://www.gtinsearch.org/api/items/0754523765792 * * @param results * * @return */ @WithSpan - @Override - public List jsonNodeToSearchResults(JsonNode results) { - log.debug("Data from Datakick: {}", results.toPrettyString()); - if (!results.isArray()) { - log.warn("Data from DataKick not an array!"); - return List.of(); - } + public Collection jsonNodeToSearchResults(ArrayNode results) { + log.debug("Data from Datakick: {}", results); - ArrayNode resultsAsArr = (ArrayNode) results; - List resultList = new ArrayList<>(resultsAsArr.size()); + List resultsList = new ArrayList<>(results.size()); - for (JsonNode result : results) { - ObjectNode curResultJson = (ObjectNode) result; + for(JsonNode curResult : results) { + ObjectNode curResultJson = (ObjectNode) curResult; String brandName = ""; String name = ""; Map attributes = new HashMap<>(); - for (Iterator> iter = curResultJson.fields(); iter.hasNext(); ) { - Map.Entry curField = iter.next(); + for (Map.Entry curField : curResultJson.properties()) { String curFieldName = curField.getKey(); String curFieldVal = curField.getValue().asText(); @@ -120,23 +112,27 @@ public List jsonNodeToSearchResults(JsonNode results) { } } - resultList.add( - ExtItemLookupResult - .builder() - .source(this.getProviderInfo().getDisplayName()) - .name(name) - .brand(brandName) - .unifiedName(name) - .attributes(attributes) - .build() + resultsList.add(ExtItemLookupResult + .builder() + .source(this.getProviderInfo().getDisplayName()) + .name(name) + .brand(brandName) + .unifiedName(name) + .attributes(attributes) + .build() ); } - - return resultList; + return resultsList; } @Override - protected CompletionStage performBarcodeSearchCall(String barcode) { - return this.dataKickLookupClient.getFromUpcCode(barcode); + public Optional> searchBarcode(String search) { + return Optional.of( + this.performBarcodeSearch(search) + .map(this::jsonNodeToSearchResults) + .onItem().transformToMulti(collection -> + Multi.createFrom().iterable(collection) + ) + ); } } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/lego/rebrickable/RebrickableLookupClient.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableLookupClient.java similarity index 72% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/lego/rebrickable/RebrickableLookupClient.java rename to software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableLookupClient.java index c2994121e5..9694098940 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/lego/rebrickable/RebrickableLookupClient.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableLookupClient.java @@ -1,7 +1,9 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.lego.rebrickable; +package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.rebrickable; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import io.opentelemetry.instrumentation.annotations.WithSpan; +import io.smallrye.mutiny.Uni; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; @@ -14,8 +16,8 @@ @RegisterRestClient(configKey = "lego-rebrickable") public interface RebrickableLookupClient { - @WithSpan - @Path("parts/{partNo}/") @GET - CompletionStage getFromPartNum(@QueryParam("key") String apiKey, @PathParam("partNo") String partNumber); + @Path("parts/{partNo}/") + @WithSpan + Uni getFromPartNum(@QueryParam("key") String apiKey, @PathParam("partNo") String partNumber); } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/lego/rebrickable/RebrickableService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java similarity index 57% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/lego/rebrickable/RebrickableService.java rename to software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java index af0749fb61..e994ec844f 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/lego/rebrickable/RebrickableService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java @@ -1,7 +1,11 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.lego.rebrickable; +package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.rebrickable; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import io.opentelemetry.instrumentation.annotations.WithSpan; +import io.quarkus.cache.CacheResult; +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import lombok.Getter; @@ -10,100 +14,92 @@ import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.rest.client.inject.RestClient; import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; -import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupResult; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.lego.LegoLookupService; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResult; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.ItemKind; -import java.net.URL; +import java.net.URI; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.concurrent.CompletionStage; +import java.util.Optional; /** * Rebrickable search service. *

- * API docs: - * - ... + * API docs: - ... */ @ApplicationScoped @Slf4j @NoArgsConstructor -public class RebrickableService extends LegoLookupService { - +public class RebrickableService extends ItemSearchService { + private static final String BRAND = "LEGO"; + @Getter ExtItemLookupProviderInfo providerInfo; RebrickableLookupClient rebrickableLookupClient; private String apiKey; - + @Inject public RebrickableService( @RestClient RebrickableLookupClient rebrickableLookupClient, - @ConfigProperty(name = "productLookup.providers.rebrickable.displayName") - String displayName, @ConfigProperty(name = "productLookup.providers.rebrickable.enabled", defaultValue = "false") boolean enabled, - @ConfigProperty(name = "productLookup.providers.rebrickable.description", defaultValue = "") - String description, - @ConfigProperty(name = "productLookup.providers.rebrickable.acceptsContributions", defaultValue = "") - boolean acceptsContributions, @ConfigProperty(name = "productLookup.providers.rebrickable.homepage", defaultValue = "") - URL homepage, - @ConfigProperty(name = "productLookup.providers.rebrickable.cost", defaultValue = "") - String cost, - @ConfigProperty(name = "productLookup.providers.rebrickable.apiKey", defaultValue = "") String apiKey ) { this.rebrickableLookupClient = rebrickableLookupClient; this.apiKey = apiKey; - + ExtItemLookupProviderInfo.Builder infoBuilder = ExtItemLookupProviderInfo - .builder() - .displayName(displayName) - .description(description) - .acceptsContributions(acceptsContributions) - .homepage(homepage) - .cost(cost); - + .builder() + .id("rebrickable") + .displayName("Rebrickable") + .description("A database of LEGO(TM) pieces. Free, but requires you to get your own key.") + .acceptsContributions(false) + .cost("Free") + .brands(List.of(BRAND)) + .kinds(List.of(ItemKind.LEGO)) + .homepage(URI.create("https://rebrickable.com")); + if (apiKey == null || apiKey.isBlank()) { - log.warn("API key for {} was null or blank.", displayName); + log.warn("API key for Rebrickable was null or blank."); infoBuilder.enabled(false); this.apiKey = null; } else { infoBuilder.enabled(enabled); this.apiKey = apiKey; } - + this.providerInfo = infoBuilder.build(); } - + @Override public boolean isEnabled() { return this.providerInfo.isEnabled() && this.apiKey != null && !this.apiKey.isBlank(); } - + @WithSpan - @Override - public List jsonNodeToSearchResults(JsonNode results) { - log.info("Search results: {}", results); + public LookupResult jsonNodeToSearchResults(ObjectNode results) { + log.info("Search result: {}", results); ExtItemLookupResult.Builder resultBuilder = ExtItemLookupResult.builder() - .source(this.getProviderInfo().getDisplayName()) - .brand("LEGO"); - + .source(this.getProviderInfo().getDisplayName()) + .brand(BRAND); + Map attributes = new HashMap<>(); - - for (Iterator> iter = results.fields(); iter.hasNext(); ) { - Map.Entry curField = iter.next(); + + for (Map.Entry curField : results.properties()) { String curFieldName = curField.getKey(); String curFieldVal = curField.getValue().asText(); - + //TODO:: handle images - + if (curField.getValue().isNull() || curFieldVal == null || curFieldVal.isBlank()) { continue; } - + switch (curFieldName) { case "name": resultBuilder.name(curFieldVal); @@ -113,15 +109,23 @@ public List jsonNodeToSearchResults(JsonNode results) { attributes.put(curFieldName, curFieldVal); } } - + resultBuilder.attributes(attributes); - - return List.of(resultBuilder.build()); + + return resultBuilder.build(); } - - @WithSpan - @Override - protected CompletionStage performPartNumberSearchCall(String partNum) { + + @CacheResult(cacheName = "rebrickable-part-num-search") + public Uni performPartNoSearch(String partNum) { return this.rebrickableLookupClient.getFromPartNum(this.apiKey, partNum); } + + @Override + public Optional> searchPartNum(String partNum) { + return Optional.of( + this.performPartNoSearch(partNum) + .map(this::jsonNodeToSearchResults) + .toMulti() + ); + } } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/ItemKind.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/ItemKind.java new file mode 100644 index 0000000000..350a5a8e14 --- /dev/null +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/ItemKind.java @@ -0,0 +1,5 @@ +package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils; + +public enum ItemKind { + LEGO +} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupType.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupType.java new file mode 100644 index 0000000000..f63c7edf8c --- /dev/null +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupType.java @@ -0,0 +1,9 @@ +package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils; + +public enum LookupType { + FREE_TEXT, + NAME, + BARCODE, + PART_NUM, + URL, +} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/webPage/AdafruitWebProductScrapeService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/webPage/AdafruitWebProductScrapeService.java index 271e4a9bb1..314b23007f 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/webPage/AdafruitWebProductScrapeService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/webPage/AdafruitWebProductScrapeService.java @@ -7,7 +7,7 @@ import lombok.extern.slf4j.Slf4j; import org.jsoup.nodes.Document; import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; -import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupResult; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResult; import java.net.URL; import java.util.HashMap; diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/webPage/AmazonWebProductScrapeService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/webPage/AmazonWebProductScrapeService.java index 9f87bc0de9..86fec95a84 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/webPage/AmazonWebProductScrapeService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/webPage/AmazonWebProductScrapeService.java @@ -7,7 +7,7 @@ import lombok.extern.slf4j.Slf4j; import org.jsoup.nodes.Document; import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; -import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupResult; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResult; import java.net.URL; import java.util.HashMap; diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/webPage/GenericWebProductScrapeService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/webPage/GenericWebProductScrapeService.java index 087e48a0e1..1e442fd5fc 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/webPage/GenericWebProductScrapeService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/webPage/GenericWebProductScrapeService.java @@ -7,7 +7,7 @@ import lombok.extern.slf4j.Slf4j; import org.jsoup.nodes.Document; import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; -import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupResult; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResult; import java.net.URL; import java.util.HashMap; diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/webPage/WebPageProductScrapeService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/webPage/WebPageProductScrapeService.java index d4af186a08..03b1355f25 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/webPage/WebPageProductScrapeService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/webPage/WebPageProductScrapeService.java @@ -2,7 +2,7 @@ import org.jsoup.Jsoup; import org.jsoup.nodes.Document; -import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupResult; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResult; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; import java.io.IOException; diff --git a/software/plugins/external-item-search/src/main/resources/application.yml b/software/plugins/external-item-search/src/main/resources/application.yml index ccaae115a6..859298c816 100644 --- a/software/plugins/external-item-search/src/main/resources/application.yml +++ b/software/plugins/external-item-search/src/main/resources/application.yml @@ -15,13 +15,7 @@ productLookup: url: https://api.barcodelookup.com/ apiKey: " " datakick: - displayName: Datakick - description: The open product database, free and open database of products. Mostly for home and food goods. Limited size of database, but free and open to contributions. - cost: Free - acceptsContributions: true - homepage: https://gtinsearch.org/ enabled: true - url: https://www.gtinsearch.org upcitemdb: displayName: upcitemdb.com description: A lookup database with good number of records, and a free tier with 100 requests per day. @@ -32,13 +26,7 @@ productLookup: url: https://api.upcitemdb.com apiKey: " " rebrickable: - displayName: Rebrickable - description: A database of LEGO(TM) pieces. Free, but requires you to get your own key. - cost: Free - acceptsContributions: false - homepage: https://rebrickable.com enabled: true - url: https://rebrickable.com apiKey: " " quarkus: @@ -61,13 +49,13 @@ quarkus: url: ${productLookup.providers.barcodelookup-com.url} scope: jakarta.inject.Singleton upc-datakick: - url: ${productLookup.providers.datakick.url} + url: https://www.gtinsearch.org scope: jakarta.inject.Singleton upc-upcitemdb: url: ${productLookup.providers.upcitemdb.url} scope: jakarta.inject.Singleton lego-rebrickable: - url: ${productLookup.providers.rebrickable.url} + url: https://rebrickable.com scope: jakarta.inject.Singleton smallrye-openapi: info-title: External Item Search API @@ -87,6 +75,12 @@ quarkus: health: openapi: included: true + wiremock: + devservices: + enabled: true + reload: true + files-mapping: ./dev/services/ + global-response-templating: true "%dev": productLookup: @@ -108,9 +102,3 @@ quarkus: ssl-port: 8443 log: level: DEBUG - wiremock: - devservices: - enabled: true - reload: true - files-mapping: ../../../../dev/wmMapping - global-response-templating: true From 8a022e61b6af452d5da63e147de96f8ae1c20d28 Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Tue, 21 Apr 2026 11:28:42 -0400 Subject: [PATCH 03/38] Plugin - Ext Item Search - Code sync for bug creation --- .../dev/services/wm_settings.json | 7 + .../interfaces/ItemLookupRestInterface.java | 3 +- .../extItemSearch/model/ExtItemSearch.java | 10 + .../lookupResult/ExtItemLookupErrResult.java | 6 + .../model/lookupResult/LookupResult.java | 2 + .../searchServices/ItemSearchService.java | 23 ++ .../barcodeLookup/BarcodeLookupClient.java | 6 +- .../barcodeLookup/BarcodeLookupService.java | 302 +++++++++--------- .../dataKick/DataKickLookupClient.java | 2 +- .../providers/dataKick/DatakickService.java | 4 +- .../rebrickable/RebrickableLookupClient.java | 2 +- .../rebrickable/RebrickableService.java | 2 +- .../src/main/resources/application.yml | 32 +- .../oqm/plugin/extItemSearch/BootTest.java | 1 + .../api/ItemApiSearchService.java | 0 .../api/product/ApiProductSearchService.java | 0 .../upcItemDb/UpcItemDbLookupClient.java | 0 .../product/upcItemDb/UpcItemDbService.java | 0 .../AdafruitWebProductScrapeService.java | 0 .../AmazonWebProductScrapeService.java | 0 .../GenericWebProductScrapeService.java | 0 .../webPage/WebPageProductScrapeService.java | 0 22 files changed, 232 insertions(+), 170 deletions(-) create mode 100644 software/plugins/external-item-search/dev/services/wm_settings.json rename software/plugins/external-item-search/{src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices => temp}/api/ItemApiSearchService.java (100%) rename software/plugins/external-item-search/{src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices => temp}/api/product/ApiProductSearchService.java (100%) rename software/plugins/external-item-search/{src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices => temp}/api/product/upcItemDb/UpcItemDbLookupClient.java (100%) rename software/plugins/external-item-search/{src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices => temp}/api/product/upcItemDb/UpcItemDbService.java (100%) rename software/plugins/external-item-search/{src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices => temp}/webPage/AdafruitWebProductScrapeService.java (100%) rename software/plugins/external-item-search/{src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices => temp}/webPage/AmazonWebProductScrapeService.java (100%) rename software/plugins/external-item-search/{src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices => temp}/webPage/GenericWebProductScrapeService.java (100%) rename software/plugins/external-item-search/{src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices => temp}/webPage/WebPageProductScrapeService.java (100%) diff --git a/software/plugins/external-item-search/dev/services/wm_settings.json b/software/plugins/external-item-search/dev/services/wm_settings.json new file mode 100644 index 0000000000..30b586a703 --- /dev/null +++ b/software/plugins/external-item-search/dev/services/wm_settings.json @@ -0,0 +1,7 @@ +{ + "delayDistribution": { + "type": "lognormal", + "median": 80, + "sigma": 0.4 + } +} \ No newline at end of file diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java index 7049843832..05c2367d25 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java @@ -4,6 +4,7 @@ import jakarta.annotation.security.PermitAll; import jakarta.enterprise.context.RequestScoped; import jakarta.inject.Inject; +import jakarta.validation.Valid; import jakarta.ws.rs.BeanParam; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; @@ -78,7 +79,7 @@ public Response allProviderInfo() { ) @PermitAll @Produces(MediaType.APPLICATION_JSON) - public Multi allProviderInfo(@BeanParam ExtItemSearch search) { + public Multi search(@Valid @BeanParam ExtItemSearch search) { return this.productLookupService.search(search); } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemSearch.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemSearch.java index f5368ccd0d..69ee282a4d 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemSearch.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemSearch.java @@ -1,9 +1,14 @@ package tech.ebp.oqm.plugin.extItemSearch.model; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import jakarta.ws.rs.QueryParam; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; import lombok.Setter; import lombok.ToString; import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; @@ -16,6 +21,8 @@ @Getter @Setter @Builder +@AllArgsConstructor +@NoArgsConstructor public class ExtItemSearch { @Parameter(description = "The kind of item to search for. If empty, will search for any item. If multiple, treated as an 'or'.") @@ -35,6 +42,9 @@ public class ExtItemSearch { List services; + @NonNull + @NotNull + @NotBlank @QueryParam("q") String search; } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupErrResult.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupErrResult.java index 64c051755d..0945032e30 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupErrResult.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupErrResult.java @@ -33,4 +33,10 @@ public ResultType getType() { } private String errMessage; + private Integer errCode; + + public String getDisplayMessage() { + return (this.getErrCode() == null? "" : this.getErrCode() + " - ") + this.errMessage; + } + } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/LookupResult.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/LookupResult.java index 65f638ce99..e1039b03ff 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/LookupResult.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/LookupResult.java @@ -5,10 +5,12 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; +import lombok.Data; import lombok.NoArgsConstructor; import lombok.NonNull; import lombok.experimental.SuperBuilder; +@Data @AllArgsConstructor @NoArgsConstructor @SuperBuilder diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/ItemSearchService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/ItemSearchService.java index 10e4adeb9e..2e024f86b0 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/ItemSearchService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/ItemSearchService.java @@ -2,14 +2,19 @@ import io.smallrye.mutiny.Multi; +import lombok.extern.slf4j.Slf4j; +import org.jboss.resteasy.reactive.ClientWebApplicationException; import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupErrResult; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupType; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Optional; +@Slf4j public abstract class ItemSearchService { public abstract ExtItemLookupProviderInfo getProviderInfo(); @@ -62,4 +67,22 @@ public Optional> search(LookupType type, String search) { }; } + + protected ExtItemLookupErrResult handleError(Throwable error) { + log.warn("Error searching for ext items: {}", error.getMessage(), error); + ExtItemLookupErrResult.Builder builder = ExtItemLookupErrResult.builder(); + builder.source(this.getProviderInfo().getId()); + + builder.errMessage(error.getMessage()); + + if(error instanceof ClientWebApplicationException) { + builder.errCode(((ClientWebApplicationException) error).getResponse().getStatus()); + builder.errMessage(((ClientWebApplicationException) error).getResponse().getStatusInfo().getReasonPhrase()); + } + + return builder.build(); + } + protected Collection handleErrorRetCollection(Throwable error) { + return List.of(this.handleError(error)); + } } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupClient.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupClient.java index 024df81f5a..23acf79091 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupClient.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupClient.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.JsonNode; import io.opentelemetry.instrumentation.annotations.WithSpan; +import io.smallrye.mutiny.Uni; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.QueryParam; @@ -10,9 +11,10 @@ import java.util.concurrent.CompletionStage; @Path("/v3/products") -@RegisterRestClient(configKey = "upc-barcodelookup-com") +@RegisterRestClient(configKey = "barcodelookup-com") public interface BarcodeLookupClient { + @WithSpan @GET - CompletionStage getFromUpcCode(@QueryParam("key") String apiKey, @QueryParam("barcode") String barcode); + Uni getFromUpcCode(@QueryParam("key") String apiKey, @QueryParam("barcode") String barcode); } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupService.java index b878ddd967..012872b688 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupService.java @@ -1,151 +1,151 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.barcodeLookup; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import io.opentelemetry.instrumentation.annotations.WithSpan; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.eclipse.microprofile.config.inject.ConfigProperty; -import org.eclipse.microprofile.rest.client.inject.RestClient; -import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; -import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResult; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.product.ApiProductSearchService; - -import java.net.URL; -import java.util.*; -import java.util.concurrent.CompletionStage; - -/** - * - * - https://www.barcodelookup.com/api - */ -@ApplicationScoped -@Slf4j -@NoArgsConstructor -public class BarcodeLookupService extends ItemSearchService { - - BarcodeLookupClient barcodeLookupClient; - @Getter - ExtItemLookupProviderInfo providerInfo; - String apiKey; - - @Inject - public BarcodeLookupService( - @RestClient - BarcodeLookupClient barcodeLookupClient, - @ConfigProperty(name = "productLookup.providers.barcodelookup-com.displayName") - String displayName, - @ConfigProperty(name = "productLookup.providers.barcodelookup-com.enabled", defaultValue = "false") - boolean enabled, - @ConfigProperty(name = "productLookup.providers.barcodelookup-com.description", defaultValue = "") - String description, - @ConfigProperty(name = "productLookup.providers.barcodelookup-com.acceptsContributions", defaultValue = "") - boolean acceptsContributions, - @ConfigProperty(name = "productLookup.providers.barcodelookup-com.homepage", defaultValue = "") - URL homepage, - @ConfigProperty(name = "productLookup.providers.barcodelookup-com.cost", defaultValue = "") - String cost, - @ConfigProperty(name = "productLookup.providers.barcodelookup-com.apiKey", defaultValue = "") - String apiKey - ) { - this.barcodeLookupClient = barcodeLookupClient; - - ExtItemLookupProviderInfo.Builder infoBuilder = ExtItemLookupProviderInfo - .builder() - .displayName(displayName) - .description(description) - .acceptsContributions(acceptsContributions) - .homepage(homepage) - .cost(cost); - - if (apiKey == null || apiKey.isBlank()) { - log.warn("API key for {} was null or blank.", displayName); - infoBuilder.enabled(false); - this.apiKey = null; - } else { - infoBuilder.enabled(enabled); - this.apiKey = apiKey; - } - - this.providerInfo = infoBuilder.build(); - } - - @Override - public boolean isEnabled() { - return this.getProviderInfo().isEnabled(); - } - - /** - * https://www.barcodelookup.com/api - * - * @param results - * - * @return - */ - @WithSpan - @Override - public List jsonNodeToSearchResults(JsonNode results) { - log.debug("Data from BarcodeLookup: {}", results.toPrettyString()); - - ArrayNode resultsAsArr = (ArrayNode) results.get("products"); - List resultList = new ArrayList<>(resultsAsArr.size()); - - for (JsonNode result : resultsAsArr) { - ObjectNode curResultJson = (ObjectNode) result; - String brandName = ""; - String name = ""; - Map attributes = new HashMap<>(); - - for (Iterator> iter = curResultJson.fields(); iter.hasNext(); ) { - Map.Entry curField = iter.next(); - - if (curField.getValue().isObject() || curField.getValue().isArray()) { - //TODO:: handle? - continue; - } - - String curFieldName = curField.getKey(); - String curFieldVal = curField.getValue().asText(); - - if (curField.getValue().isNull() || curFieldVal == null || curFieldVal.isBlank()) { - continue; - } - - switch (curFieldName) { - case "brand": - brandName = curFieldVal; - break; - case "title": - name = curFieldVal; - break; - default: - attributes.put(curFieldName, curFieldVal); - } - } - - resultList.add( - ExtItemLookupResult - .builder() - .source(this.getProviderInfo().getDisplayName()) - .name(name) - .brand(brandName) - .unifiedName(name) - .attributes(attributes) - .build() - ); - } - - return resultList; - } - - @WithSpan - @Override - protected CompletionStage performBarcodeSearchCall(String barcode) { - return this.barcodeLookupClient.getFromUpcCode(this.apiKey, barcode); - } -} +//package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.barcodeLookup; +// +//import com.fasterxml.jackson.databind.JsonNode; +//import com.fasterxml.jackson.databind.node.ArrayNode; +//import com.fasterxml.jackson.databind.node.ObjectNode; +//import io.opentelemetry.instrumentation.annotations.WithSpan; +//import jakarta.enterprise.context.ApplicationScoped; +//import jakarta.inject.Inject; +//import lombok.Getter; +//import lombok.NoArgsConstructor; +//import lombok.extern.slf4j.Slf4j; +//import org.eclipse.microprofile.config.inject.ConfigProperty; +//import org.eclipse.microprofile.rest.client.inject.RestClient; +//import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; +//import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResult; +//import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; +//import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.product.ApiProductSearchService; +// +//import java.net.URL; +//import java.util.*; +//import java.util.concurrent.CompletionStage; +// +///** +// * +// * - https://www.barcodelookup.com/api +// */ +//@ApplicationScoped +//@Slf4j +//@NoArgsConstructor +//public class BarcodeLookupService extends ItemSearchService { +// +// BarcodeLookupClient barcodeLookupClient; +// @Getter +// ExtItemLookupProviderInfo providerInfo; +// String apiKey; +// +// @Inject +// public BarcodeLookupService( +// @RestClient +// BarcodeLookupClient barcodeLookupClient, +// @ConfigProperty(name = "productLookup.providers.barcodelookup-com.displayName") +// String displayName, +// @ConfigProperty(name = "productLookup.providers.barcodelookup-com.enabled", defaultValue = "false") +// boolean enabled, +// @ConfigProperty(name = "productLookup.providers.barcodelookup-com.description", defaultValue = "") +// String description, +// @ConfigProperty(name = "productLookup.providers.barcodelookup-com.acceptsContributions", defaultValue = "") +// boolean acceptsContributions, +// @ConfigProperty(name = "productLookup.providers.barcodelookup-com.homepage", defaultValue = "") +// URL homepage, +// @ConfigProperty(name = "productLookup.providers.barcodelookup-com.cost", defaultValue = "") +// String cost, +// @ConfigProperty(name = "productLookup.providers.barcodelookup-com.apiKey", defaultValue = "") +// String apiKey +// ) { +// this.barcodeLookupClient = barcodeLookupClient; +// +// ExtItemLookupProviderInfo.Builder infoBuilder = ExtItemLookupProviderInfo +// .builder() +// .displayName(displayName) +// .description(description) +// .acceptsContributions(acceptsContributions) +// .homepage(homepage) +// .cost(cost); +// +// if (apiKey == null || apiKey.isBlank()) { +// log.warn("API key for {} was null or blank.", displayName); +// infoBuilder.enabled(false); +// this.apiKey = null; +// } else { +// infoBuilder.enabled(enabled); +// this.apiKey = apiKey; +// } +// +// this.providerInfo = infoBuilder.build(); +// } +// +// @Override +// public boolean isEnabled() { +// return this.getProviderInfo().isEnabled(); +// } +// +// /** +// * https://www.barcodelookup.com/api +// * +// * @param results +// * +// * @return +// */ +// @WithSpan +// @Override +// public List jsonNodeToSearchResults(JsonNode results) { +// log.debug("Data from BarcodeLookup: {}", results.toPrettyString()); +// +// ArrayNode resultsAsArr = (ArrayNode) results.get("products"); +// List resultList = new ArrayList<>(resultsAsArr.size()); +// +// for (JsonNode result : resultsAsArr) { +// ObjectNode curResultJson = (ObjectNode) result; +// String brandName = ""; +// String name = ""; +// Map attributes = new HashMap<>(); +// +// for (Iterator> iter = curResultJson.fields(); iter.hasNext(); ) { +// Map.Entry curField = iter.next(); +// +// if (curField.getValue().isObject() || curField.getValue().isArray()) { +// //TODO:: handle? +// continue; +// } +// +// String curFieldName = curField.getKey(); +// String curFieldVal = curField.getValue().asText(); +// +// if (curField.getValue().isNull() || curFieldVal == null || curFieldVal.isBlank()) { +// continue; +// } +// +// switch (curFieldName) { +// case "brand": +// brandName = curFieldVal; +// break; +// case "title": +// name = curFieldVal; +// break; +// default: +// attributes.put(curFieldName, curFieldVal); +// } +// } +// +// resultList.add( +// ExtItemLookupResult +// .builder() +// .source(this.getProviderInfo().getDisplayName()) +// .name(name) +// .brand(brandName) +// .unifiedName(name) +// .attributes(attributes) +// .build() +// ); +// } +// +// return resultList; +// } +// +// @WithSpan +// @Override +// protected CompletionStage performBarcodeSearchCall(String barcode) { +// return this.barcodeLookupClient.getFromUpcCode(this.apiKey, barcode); +// } +//} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DataKickLookupClient.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DataKickLookupClient.java index 3d8f6f7493..93e1882ea2 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DataKickLookupClient.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DataKickLookupClient.java @@ -14,7 +14,7 @@ import java.util.concurrent.CompletionStage; @Path("/api/items/") -@RegisterRestClient(configKey = "upc-datakick") +@RegisterRestClient(configKey = "datakick") public interface DataKickLookupClient { @WithSpan diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java index 2d8464cec7..31584feabc 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java @@ -57,7 +57,7 @@ public DatakickService( .build(); } - @CacheResult(cacheName = "rebrickable-part-num-search") + @CacheResult(cacheName = "datakick-barcode-search") public Uni performBarcodeSearch(String barcode) { return this.dataKickLookupClient.getFromUpcCode(barcode); } @@ -130,6 +130,8 @@ public Optional> searchBarcode(String search) { return Optional.of( this.performBarcodeSearch(search) .map(this::jsonNodeToSearchResults) + .onFailure().recoverWithItem(this::handleErrorRetCollection) + .onItem().transformToMulti(collection -> Multi.createFrom().iterable(collection) ) diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableLookupClient.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableLookupClient.java index 9694098940..e3c70ae0bf 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableLookupClient.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableLookupClient.java @@ -13,7 +13,7 @@ import java.util.concurrent.CompletionStage; @Path("/api/v3/lego/") -@RegisterRestClient(configKey = "lego-rebrickable") +@RegisterRestClient(configKey = "rebrickable") public interface RebrickableLookupClient { @GET diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java index e994ec844f..e72e0aefe7 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java @@ -47,7 +47,7 @@ public RebrickableService( RebrickableLookupClient rebrickableLookupClient, @ConfigProperty(name = "productLookup.providers.rebrickable.enabled", defaultValue = "false") boolean enabled, - @ConfigProperty(name = "productLookup.providers.rebrickable.homepage", defaultValue = "") + @ConfigProperty(name = "productLookup.providers.rebrickable.apiKey", defaultValue = "") String apiKey ) { this.rebrickableLookupClient = rebrickableLookupClient; diff --git a/software/plugins/external-item-search/src/main/resources/application.yml b/software/plugins/external-item-search/src/main/resources/application.yml index 859298c816..76ff90acbc 100644 --- a/software/plugins/external-item-search/src/main/resources/application.yml +++ b/software/plugins/external-item-search/src/main/resources/application.yml @@ -45,16 +45,16 @@ quarkus: rest-client: logging: scope: all - upc-barcodelookup-com: - url: ${productLookup.providers.barcodelookup-com.url} + barcodelookup-com: + url: https://api.barcodelookup.com/ scope: jakarta.inject.Singleton - upc-datakick: + datakick: url: https://www.gtinsearch.org scope: jakarta.inject.Singleton - upc-upcitemdb: - url: ${productLookup.providers.upcitemdb.url} + upcitemdb: + url: https://api.upcitemdb.com scope: jakarta.inject.Singleton - lego-rebrickable: + rebrickable: url: https://rebrickable.com scope: jakarta.inject.Singleton smallrye-openapi: @@ -86,14 +86,8 @@ quarkus: productLookup: providers: rebrickable: - url: http://localhost:${quarkus.wiremock.devservices.port}/rebrickable/ apiKey: devKey - upcitemdb: - url: http://localhost:${quarkus.wiremock.devservices.port}/upcitemdb/ - datakick: - url: http://localhost:${quarkus.wiremock.devservices.port}/datakick/ barcodelookup-com: - url: http://localhost:${quarkus.wiremock.devservices.port}/barcodelookup/ apiKey: devKey quarkus: @@ -102,3 +96,17 @@ quarkus: ssl-port: 8443 log: level: DEBUG + rest-client: + logging: + scope: all + barcodelookup-com: + url: http://localhost:${quarkus.wiremock.devservices.port}/barcodelookup/ + scope: jakarta.inject.Singleton + datakick: + url: http://localhost:${quarkus.wiremock.devservices.port}/datakick/ + scope: jakarta.inject.Singleton + upcitemdb: + url: http://localhost:${quarkus.wiremock.devservices.port}/upcitemdb/ + scope: jakarta.inject.Singleton + rebrickable: + url: http://localhost:${quarkus.wiremock.devservices.port}/rebrickable/ diff --git a/software/plugins/external-item-search/src/test/java/tech/ebp/oqm/plugin/extItemSearch/BootTest.java b/software/plugins/external-item-search/src/test/java/tech/ebp/oqm/plugin/extItemSearch/BootTest.java index 2a9b467e54..7518a93185 100644 --- a/software/plugins/external-item-search/src/test/java/tech/ebp/oqm/plugin/extItemSearch/BootTest.java +++ b/software/plugins/external-item-search/src/test/java/tech/ebp/oqm/plugin/extItemSearch/BootTest.java @@ -10,6 +10,7 @@ class BootTest { @Test void testHelloEndpoint() { + given() .when().get("/api/v1/providers") .then() diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/ItemApiSearchService.java b/software/plugins/external-item-search/temp/api/ItemApiSearchService.java similarity index 100% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/ItemApiSearchService.java rename to software/plugins/external-item-search/temp/api/ItemApiSearchService.java diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/ApiProductSearchService.java b/software/plugins/external-item-search/temp/api/product/ApiProductSearchService.java similarity index 100% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/ApiProductSearchService.java rename to software/plugins/external-item-search/temp/api/product/ApiProductSearchService.java diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/upcItemDb/UpcItemDbLookupClient.java b/software/plugins/external-item-search/temp/api/product/upcItemDb/UpcItemDbLookupClient.java similarity index 100% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/upcItemDb/UpcItemDbLookupClient.java rename to software/plugins/external-item-search/temp/api/product/upcItemDb/UpcItemDbLookupClient.java diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/upcItemDb/UpcItemDbService.java b/software/plugins/external-item-search/temp/api/product/upcItemDb/UpcItemDbService.java similarity index 100% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/api/product/upcItemDb/UpcItemDbService.java rename to software/plugins/external-item-search/temp/api/product/upcItemDb/UpcItemDbService.java diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/webPage/AdafruitWebProductScrapeService.java b/software/plugins/external-item-search/temp/webPage/AdafruitWebProductScrapeService.java similarity index 100% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/webPage/AdafruitWebProductScrapeService.java rename to software/plugins/external-item-search/temp/webPage/AdafruitWebProductScrapeService.java diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/webPage/AmazonWebProductScrapeService.java b/software/plugins/external-item-search/temp/webPage/AmazonWebProductScrapeService.java similarity index 100% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/webPage/AmazonWebProductScrapeService.java rename to software/plugins/external-item-search/temp/webPage/AmazonWebProductScrapeService.java diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/webPage/GenericWebProductScrapeService.java b/software/plugins/external-item-search/temp/webPage/GenericWebProductScrapeService.java similarity index 100% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/webPage/GenericWebProductScrapeService.java rename to software/plugins/external-item-search/temp/webPage/GenericWebProductScrapeService.java diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/webPage/WebPageProductScrapeService.java b/software/plugins/external-item-search/temp/webPage/WebPageProductScrapeService.java similarity index 100% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/webPage/WebPageProductScrapeService.java rename to software/plugins/external-item-search/temp/webPage/WebPageProductScrapeService.java From e6e960143425aae95e203c25c3d5218de9183a90 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 20:23:54 +0000 Subject: [PATCH 04/38] Bump io.freefair.lombok in /software/plugins/external-item-search Bumps [io.freefair.lombok](https://github.com/freefair/gradle-plugins) from 9.2.0 to 9.4.0. - [Release notes](https://github.com/freefair/gradle-plugins/releases) - [Commits](https://github.com/freefair/gradle-plugins/compare/9.2.0...9.4.0) --- updated-dependencies: - dependency-name: io.freefair.lombok dependency-version: 9.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- software/plugins/external-item-search/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/software/plugins/external-item-search/build.gradle b/software/plugins/external-item-search/build.gradle index 21aca7ddf8..74855b13ae 100644 --- a/software/plugins/external-item-search/build.gradle +++ b/software/plugins/external-item-search/build.gradle @@ -1,7 +1,7 @@ plugins { id 'java' id 'io.quarkus' - id "io.freefair.lombok" version "9.2.0" + id "io.freefair.lombok" version "9.4.0" } group 'tech.ebp.openQuarterMaster' From 4f7d936cbf96b4b0e7126f0c52362c461bb88c93 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 20:23:57 +0000 Subject: [PATCH 05/38] Bump org.jsoup:jsoup in /software/plugins/external-item-search Bumps [org.jsoup:jsoup](https://github.com/jhy/jsoup) from 1.22.1 to 1.22.2. - [Release notes](https://github.com/jhy/jsoup/releases) - [Changelog](https://github.com/jhy/jsoup/blob/master/CHANGES.md) - [Commits](https://github.com/jhy/jsoup/compare/jsoup-1.22.1...jsoup-1.22.2) --- updated-dependencies: - dependency-name: org.jsoup:jsoup dependency-version: 1.22.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- software/plugins/external-item-search/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/software/plugins/external-item-search/build.gradle b/software/plugins/external-item-search/build.gradle index 21aca7ddf8..a72fb95ef1 100644 --- a/software/plugins/external-item-search/build.gradle +++ b/software/plugins/external-item-search/build.gradle @@ -28,7 +28,7 @@ dependencies { implementation 'io.quarkus:quarkus-container-image-docker' implementation 'io.quarkus:quarkus-arc' - implementation group: 'org.jsoup', name: 'jsoup', version: '1.22.1' + implementation group: 'org.jsoup', name: 'jsoup', version: '1.22.2' implementation 'io.quarkiverse.wiremock:quarkus-wiremock:1.6.1' From 8f0c9948d16e1c3c95dd512febaee2fdad1db810 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 20:24:51 +0000 Subject: [PATCH 06/38] Bump io.freefair.lombok in /software/core/oqm-core-api Bumps [io.freefair.lombok](https://github.com/freefair/gradle-plugins) from 9.2.0 to 9.4.0. - [Release notes](https://github.com/freefair/gradle-plugins/releases) - [Commits](https://github.com/freefair/gradle-plugins/compare/9.2.0...9.4.0) --- updated-dependencies: - dependency-name: io.freefair.lombok dependency-version: 9.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- software/core/oqm-core-api/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/software/core/oqm-core-api/build.gradle b/software/core/oqm-core-api/build.gradle index 6584029e21..95ee3a38f2 100644 --- a/software/core/oqm-core-api/build.gradle +++ b/software/core/oqm-core-api/build.gradle @@ -1,7 +1,7 @@ plugins { id 'java' id 'io.quarkus' - id "io.freefair.lombok" version "9.2.0" + id "io.freefair.lombok" version "9.4.0" id 'jacoco' id 'org.cyclonedx.bom' version '3.2.4' id("org.spdx.sbom") version "0.10.0" From 70eefcccd8696b0a1838632ac073b2d63b21dd5b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 20:24:57 +0000 Subject: [PATCH 07/38] Bump com.deque.html.axe-core:playwright Bumps [com.deque.html.axe-core:playwright](https://github.com/dequelabs/axe-core-maven-html) from 4.11.1 to 4.11.2. - [Release notes](https://github.com/dequelabs/axe-core-maven-html/releases) - [Changelog](https://github.com/dequelabs/axe-core-maven-html/blob/v4.11.2/CHANGELOG.md) - [Commits](https://github.com/dequelabs/axe-core-maven-html/compare/v4.11.1...v4.11.2) --- updated-dependencies: - dependency-name: com.deque.html.axe-core:playwright dependency-version: 4.11.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- software/core/oqm-core-base-station/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/software/core/oqm-core-base-station/build.gradle b/software/core/oqm-core-base-station/build.gradle index f42c6918af..b20eccf7d3 100644 --- a/software/core/oqm-core-base-station/build.gradle +++ b/software/core/oqm-core-base-station/build.gradle @@ -49,7 +49,7 @@ dependencies { testImplementation 'net.datafaker:datafaker:2.5.4' testImplementation 'com.microsoft.playwright:playwright:1.59.0' - testImplementation 'com.deque.html.axe-core:playwright:4.11.1' + testImplementation 'com.deque.html.axe-core:playwright:4.11.2' } java { From f5422435de70f7d17c07dd8008ac638518e040b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 20:26:09 +0000 Subject: [PATCH 08/38] Bump io.freefair.lombok in /software/plugins/storagotchi Bumps [io.freefair.lombok](https://github.com/freefair/gradle-plugins) from 9.2.0 to 9.4.0. - [Release notes](https://github.com/freefair/gradle-plugins/releases) - [Commits](https://github.com/freefair/gradle-plugins/compare/9.2.0...9.4.0) --- updated-dependencies: - dependency-name: io.freefair.lombok dependency-version: 9.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- software/plugins/storagotchi/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/software/plugins/storagotchi/build.gradle b/software/plugins/storagotchi/build.gradle index 6af6f5592c..f3286bd546 100644 --- a/software/plugins/storagotchi/build.gradle +++ b/software/plugins/storagotchi/build.gradle @@ -1,7 +1,7 @@ plugins { id 'java' id 'io.quarkus' - id "io.freefair.lombok" version "9.2.0" + id "io.freefair.lombok" version "9.4.0" } group = 'tech.ebp.oqm.plugin' From b7b71d0b2d80db50bf164d2febbda5a0db2dd677 Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Tue, 21 Apr 2026 23:05:18 -0400 Subject: [PATCH 09/38] Plugin - Ext Item Search - Streamlining setup, working --- .../old/mappings/barcodelookup_barcode.json | 372 ++++++++++++++++++ .../dev/old/mappings/upcitemdb_barcode.json | 23 ++ .../mappings/upcitemdb_trial_barcode.json | 0 .../services/datakick/datakick_barcode.json | 49 --- .../dev/services/mappings/README.md | 11 + .../mappings/barcodelookup_barcode.json | 372 ------------------ .../{ => mappings}/datakick/README.md | 0 .../mappings/datakick/datakick_barcode.json | 54 +++ .../datakick/datakick_barcode_not_found.json | 22 ++ .../{ => mappings}/rebrickable/README.md | 0 .../rebrickable/rebrickable_part.json | 56 +++ .../rebrickable/rebrickable_part_bad_key.json | 29 ++ .../rebrickable_part_not_found.json | 29 ++ .../services/mappings/upcitemdb_barcode.json | 23 -- .../rebrickable/rebrickable_part.json | 46 --- .../dev/services/wm_settings.json | 2 +- .../interfaces/ItemLookupRestInterface.java | 3 +- .../lookupResult/ExtItemLookupResult.java | 11 +- .../model/lookupResult/LookupResult.java | 8 +- .../lookupResult/LookupResultNoResults.java | 36 ++ .../model/lookupResult/ResultType.java | 3 +- .../searchServices/ItemSearchService.java | 39 +- .../dataKick/DataKickLookupClient.java | 2 + .../providers/dataKick/DatakickService.java | 33 +- .../rebrickable/RebrickableLookupClient.java | 5 +- .../rebrickable/RebrickableService.java | 101 ++++- 26 files changed, 793 insertions(+), 536 deletions(-) create mode 100644 software/plugins/external-item-search/dev/old/mappings/barcodelookup_barcode.json create mode 100644 software/plugins/external-item-search/dev/old/mappings/upcitemdb_barcode.json rename software/plugins/external-item-search/dev/{services => old}/mappings/upcitemdb_trial_barcode.json (100%) delete mode 100644 software/plugins/external-item-search/dev/services/datakick/datakick_barcode.json create mode 100644 software/plugins/external-item-search/dev/services/mappings/README.md delete mode 100644 software/plugins/external-item-search/dev/services/mappings/barcodelookup_barcode.json rename software/plugins/external-item-search/dev/services/{ => mappings}/datakick/README.md (100%) create mode 100644 software/plugins/external-item-search/dev/services/mappings/datakick/datakick_barcode.json create mode 100644 software/plugins/external-item-search/dev/services/mappings/datakick/datakick_barcode_not_found.json rename software/plugins/external-item-search/dev/services/{ => mappings}/rebrickable/README.md (100%) create mode 100644 software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part.json create mode 100644 software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part_bad_key.json create mode 100644 software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part_not_found.json delete mode 100644 software/plugins/external-item-search/dev/services/mappings/upcitemdb_barcode.json delete mode 100644 software/plugins/external-item-search/dev/services/rebrickable/rebrickable_part.json create mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/LookupResultNoResults.java diff --git a/software/plugins/external-item-search/dev/old/mappings/barcodelookup_barcode.json b/software/plugins/external-item-search/dev/old/mappings/barcodelookup_barcode.json new file mode 100644 index 0000000000..4abe34fcea --- /dev/null +++ b/software/plugins/external-item-search/dev/old/mappings/barcodelookup_barcode.json @@ -0,0 +1,372 @@ +{ + "request": { + "urlPathPattern": "/v3/products", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "jsonBody": { + "products": [ + { + "barcode_number": "9781492057895", + "barcode_formats": "ISBN-10 1492057894, ISBN-13 9781492057895", + "mpn": "83017185", + "model": "", + "asin": "", + "title": "Building Event-Driven Microservices - by Adam Bellemare (Paperback)", + "category": "Media > Books", + "manufacturer": "O'Reilly Media", + "brand": "O'Reilly Media", + "contributors": [], + "age_group": "", + "ingredients": "", + "nutrition_facts": "", + "energy_efficiency_class": "", + "color": "", + "gender": "", + "material": "", + "pattern": "", + "format": "", + "multipack": "", + "size": "0.68 x 9.19 x 1", + "length": "", + "width": "", + "height": "", + "weight": "", + "release_date": "", + "description": "Building Event-Driven Microservices - by Adam Bellemare (Paperback).", + "features": [], + "images": [ + "https://images.barcodelookup.com/17022/170222173-1.jpg" + ], + "last_update": "2024-06-01 16:59:56", + "stores": [ + { + "name": "AbeBooks", + "country": "US", + "currency": "USD", + "currency_symbol": "$", + "price": "27.30", + "sale_price": "", + "tax": [], + "link": "https://www.abebooks.com/servlet/BookDetailsPL?bi=31692144717", + "item_group_id": "", + "availability": "", + "condition": "used", + "shipping": [], + "last_update": "2024-05-31 05:32:16" + }, + { + "name": "Wordery (US)", + "country": "GB", + "currency": "GBP", + "currency_symbol": "£", + "price": "28.89", + "sale_price": "", + "tax": [], + "link": "https://wordery.com/building-event-driven-microservices-adam-bellemare-9781492057895", + "item_group_id": "", + "availability": "in stock", + "condition": "new", + "shipping": [], + "last_update": "2021-08-19 21:31:14" + }, + { + "name": "Studibuch Shop DE", + "country": "EU", + "currency": "EUR", + "currency_symbol": "€", + "price": "31.31", + "sale_price": "", + "tax": [], + "link": "https://shop.studibuch.de/9781492057895/building-event-driven-microservices", + "item_group_id": "", + "availability": "in stock", + "condition": "", + "shipping": [], + "last_update": "2024-04-19 16:21:14" + }, + { + "name": "VitalSource", + "country": "AU", + "currency": "AUD", + "currency_symbol": "$", + "price": "34.50", + "sale_price": "", + "tax": [], + "link": "https://www.vitalsource.com/en-uk/products/building-event-driven-microservices-adam-bellemare-v9781492057840?duration=perpetual", + "item_group_id": "", + "availability": "in stock", + "condition": "new", + "shipping": [], + "last_update": "2024-05-06 23:44:20" + }, + { + "name": "Barnes & Noble", + "country": "US", + "currency": "USD", + "currency_symbol": "$", + "price": "36.32", + "sale_price": "", + "tax": [], + "link": "https://www.barnesandnoble.com/w/building-event-driven-microservices-adam-bellemare/1136377083?ean=9781492057895", + "item_group_id": "", + "availability": "", + "condition": "", + "shipping": [], + "last_update": "2021-06-22 04:36:10" + }, + { + "name": "Target", + "country": "US", + "currency": "USD", + "currency_symbol": "$", + "price": "36.49", + "sale_price": "", + "tax": [], + "link": "https://www.target.com/p/building-event-driven-microservices-by-adam-bellemare-paperback/-/A-83017185", + "item_group_id": "", + "availability": "", + "condition": "new", + "shipping": [], + "last_update": "2024-06-01 16:59:56" + }, + { + "name": "Walmart", + "country": "US", + "currency": "USD", + "currency_symbol": "$", + "price": "35.99", + "sale_price": "", + "tax": [], + "link": "https://www.walmart.com/ip/Building-Event-Driven-Microservices-Leveraging-Organizational-Data-at-Scale-Paperback-9781492057895/474384970", + "item_group_id": "", + "availability": "", + "condition": "", + "shipping": [], + "last_update": "2024-02-13 07:28:49" + }, + { + "name": "Hive Books", + "country": "GB", + "currency": "GBP", + "currency_symbol": "£", + "price": "41.35", + "sale_price": "", + "tax": [], + "link": "https://track.flexlinks.com/p.ashx?foc=101&fopid=1199128.171822.156100.57B0.F9670628132FAC0F.9781492057895", + "item_group_id": "", + "availability": "", + "condition": "new", + "shipping": [], + "last_update": "2024-05-19 03:22:49" + }, + { + "name": "Knetbooks.com", + "country": "US", + "currency": "USD", + "currency_symbol": "$", + "price": "44.54", + "sale_price": "", + "tax": [], + "link": "http://www.knetbooks.com/search-results?terms=9781492057895&referrer=KBCJ", + "item_group_id": "", + "availability": "in stock", + "condition": "new", + "shipping": [], + "last_update": "2023-12-13 03:00:01" + }, + { + "name": "eCampus.com", + "country": "US", + "currency": "USD", + "currency_symbol": "$", + "price": "44.99", + "sale_price": "", + "tax": [], + "link": "http://www.ecampus.com/bk_detail.asp?isbn=9781492057895&referrer=CJ", + "item_group_id": "", + "availability": "", + "condition": "new", + "shipping": [], + "last_update": "2023-05-09 22:11:15" + }, + { + "name": "Rarewaves", + "country": "GB", + "currency": "GBP", + "currency_symbol": "£", + "price": "45.32", + "sale_price": "", + "tax": [], + "link": "https://www.rarewaves.com/products/9781492057895-building-event-driven-microser?variant=32564063469665¤cy=GBP&utm_medium=affiliate&utm_source=awin&utm_term=Paperback", + "item_group_id": "", + "availability": "in stock", + "condition": "new", + "shipping": [], + "last_update": "2024-04-18 22:13:09" + }, + { + "name": "BiggerBooks.com", + "country": "US", + "currency": "USD", + "currency_symbol": "$", + "price": "46.34", + "sale_price": "", + "tax": [], + "link": "http://www.biggerbooks.com/bk_detail.asp?isbn=9781492057895&referrer=BBCJ", + "item_group_id": "", + "availability": "in stock", + "condition": "new", + "shipping": [], + "last_update": "2024-04-28 02:44:18" + }, + { + "name": "The Book Depository", + "country": "GB", + "currency": "GBP", + "currency_symbol": "£", + "price": "47.99", + "sale_price": "", + "tax": [], + "link": "https://www.bookdepository.com/Building-Event-Driven-Microservices-Adam-Bellemare/9781492057895?redirected=true&utm_medium=Google&utm_campaign=Base1&utm_source=UK&utm_content=Building-Event-Driven-Microservices&selectCurrency=GBP&w=AFC7AU96Q2NTLHA8VTJV", + "item_group_id": "", + "availability": "", + "condition": "new", + "shipping": [], + "last_update": "2022-10-25 11:46:30" + }, + { + "name": "Foyles for books", + "country": "GB", + "currency": "GBP", + "currency_symbol": "£", + "price": "52.99", + "sale_price": "", + "tax": [], + "link": "https://www.foyles.co.uk/book/building-event-driven-microservices/adam-bellemare//9781492057895", + "item_group_id": "", + "availability": "in stock", + "condition": "new", + "shipping": [], + "last_update": "2023-11-16 12:46:22" + }, + { + "name": "averdo DE", + "country": "EU", + "currency": "EUR", + "currency_symbol": "€", + "price": "53.69", + "sale_price": "", + "tax": [], + "link": "https://shop.averdo.com/product/92797154", + "item_group_id": "", + "availability": "in stock", + "condition": "new", + "shipping": [], + "last_update": "2022-07-16 17:18:02" + }, + { + "name": "Foyles for books (UK) (1414)_Closing", + "country": "GB", + "currency": "GBP", + "currency_symbol": "£", + "price": "52.99", + "sale_price": "", + "tax": [], + "link": "https://www.foyles.co.uk/book/building-event-driven-microservices/adam-bellemare//9781492057895", + "item_group_id": "", + "availability": "in stock", + "condition": "new", + "shipping": [], + "last_update": "2024-04-15 08:28:54" + }, + { + "name": "The Book Depository (US)", + "country": "US", + "currency": "USD", + "currency_symbol": "$", + "price": "59.99", + "sale_price": "", + "tax": [], + "link": "https://www.bookdepository.com/Building-Event-Driven-Microservices-Adam-Bellemare/9781492057895?redirected=true&utm_medium=Google&utm_campaign=Base1&utm_source=US&utm_content=Building-Event-Driven-Microservices&selectCurrency=USD&w=AFCCAU96Q2NTLHA8VT1N", + "item_group_id": "", + "availability": "in stock", + "condition": "new", + "shipping": [], + "last_update": "2022-07-16 05:35:02" + }, + { + "name": "Medimops DE", + "country": "EU", + "currency": "EUR", + "currency_symbol": "€", + "price": "62.00", + "sale_price": "", + "tax": [], + "link": "https://t.neory-tm.net/tm/a/container/redirect/94fca0563d.rd?pubType=!!!publishertagid!!!&touchpoint=!!!awc!!!&pubId=!!!affid!!!&tmrde=", + "item_group_id": "", + "availability": "in stock", + "condition": "", + "shipping": [], + "last_update": "2022-02-28 00:25:52" + }, + { + "name": "Angus & Robertson", + "country": "US", + "currency": "USD", + "currency_symbol": "$", + "price": "78.75", + "sale_price": "", + "tax": [], + "link": "https://www.angusrobertson.com.au/books/building-event-driven-microservices-adam-bellemare/p/9781492057895", + "item_group_id": "", + "availability": "", + "condition": "", + "shipping": [], + "last_update": "2021-06-22 04:28:14" + }, + { + "name": "Indigo Books & Music", + "country": "CA", + "currency": "CAD", + "currency_symbol": "$", + "price": "79.99", + "sale_price": "", + "tax": [], + "link": "https://www.chapters.indigo.ca/en-ca/books/product/9781492057895-item.html", + "item_group_id": "", + "availability": "", + "condition": "new", + "shipping": [], + "last_update": "2023-05-17 08:39:24" + }, + { + "name": "Elefant.ro", + "country": "RO", + "currency": "RON", + "currency_symbol": "L", + "price": "325.99", + "sale_price": "324.99", + "tax": [], + "link": "https://www.elefant.ro/building-event-driven-microservices-leveraging-organizational-data-at-scale-paperback_854bef99-d6a1-425e-b52e-28e72ba06dc7", + "item_group_id": "", + "availability": "in stock", + "condition": "new", + "shipping": [], + "last_update": "2024-04-29 17:01:06" + } + ], + "reviews": [] + } + ] + } + } +} + + + + diff --git a/software/plugins/external-item-search/dev/old/mappings/upcitemdb_barcode.json b/software/plugins/external-item-search/dev/old/mappings/upcitemdb_barcode.json new file mode 100644 index 0000000000..0e017783ff --- /dev/null +++ b/software/plugins/external-item-search/dev/old/mappings/upcitemdb_barcode.json @@ -0,0 +1,23 @@ +{ + "request": { + "urlPathPattern": "/upcitemdb/prod/v1/lookup", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "jsonBody": { + "code": "OK", + "total": 0, + "offset": 0, + "items": [ + ] + } + } +} + + + + diff --git a/software/plugins/external-item-search/dev/services/mappings/upcitemdb_trial_barcode.json b/software/plugins/external-item-search/dev/old/mappings/upcitemdb_trial_barcode.json similarity index 100% rename from software/plugins/external-item-search/dev/services/mappings/upcitemdb_trial_barcode.json rename to software/plugins/external-item-search/dev/old/mappings/upcitemdb_trial_barcode.json diff --git a/software/plugins/external-item-search/dev/services/datakick/datakick_barcode.json b/software/plugins/external-item-search/dev/services/datakick/datakick_barcode.json deleted file mode 100644 index 98ee742226..0000000000 --- a/software/plugins/external-item-search/dev/services/datakick/datakick_barcode.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "request": { - "urlPathPattern": "/datakick/api/items/.*", - "method": "GET" - }, - "response": { - "status": 200, - "headers": { - "Content-Type": "application/json" - }, - "jsonBody": [ - { - "id": 4712, - "user_id": 0, - "gtin14": "{{request.pathSegments.[4]}}", - "brand_name": "Ubisoft", - "name": "Assassin's Creed Syndicate (PS4)", - "size": "", - "ingredients": "", - "serving_size": "", - "serving_per_container": "", - "calories": "", - "fat_calories": "", - "fat": "", - "saturated_fat": "", - "trans_fat": "", - "polyunsaturated_fat": "", - "monounsaturated_fat": "", - "cholesterol": "", - "sodium": "", - "potassium": "", - "carbohydrate": "", - "fiber": "", - "sugar": "", - "protein": "", - "author": "", - "publisher": "", - "pages": "", - "alcohol": "", - "created_at": null, - "updated_at": null - } - ] - } -} - - - - diff --git a/software/plugins/external-item-search/dev/services/mappings/README.md b/software/plugins/external-item-search/dev/services/mappings/README.md new file mode 100644 index 0000000000..4200a1ece9 --- /dev/null +++ b/software/plugins/external-item-search/dev/services/mappings/README.md @@ -0,0 +1,11 @@ +# Service Mappings + +The json documents in this directory outline the responses given from the various data providers we support. They are wiremock stibs that simulate that bhavior. + +## Conventions + + - API Keys + - `devKey` is always used as a valid API key, if applicable + - `devKeyBad` is always used as an INvalid API key, in turn + + diff --git a/software/plugins/external-item-search/dev/services/mappings/barcodelookup_barcode.json b/software/plugins/external-item-search/dev/services/mappings/barcodelookup_barcode.json deleted file mode 100644 index 9f7c69ed18..0000000000 --- a/software/plugins/external-item-search/dev/services/mappings/barcodelookup_barcode.json +++ /dev/null @@ -1,372 +0,0 @@ -{ - "request": { - "urlPathPattern": "/v3/products", - "method": "GET" - }, - "response": { - "status": 200, - "headers": { - "Content-Type": "application/json" - }, - "jsonBody": { - "products": [ - { - "barcode_number": "9781492057895", - "barcode_formats": "ISBN-10 1492057894, ISBN-13 9781492057895", - "mpn": "83017185", - "model": "", - "asin": "", - "title": "Building Event-Driven Microservices - by Adam Bellemare (Paperback)", - "category": "Media > Books", - "manufacturer": "O'Reilly Media", - "brand": "O'Reilly Media", - "contributors": [], - "age_group": "", - "ingredients": "", - "nutrition_facts": "", - "energy_efficiency_class": "", - "color": "", - "gender": "", - "material": "", - "pattern": "", - "format": "", - "multipack": "", - "size": "0.68 x 9.19 x 1", - "length": "", - "width": "", - "height": "", - "weight": "", - "release_date": "", - "description": "Building Event-Driven Microservices - by Adam Bellemare (Paperback).", - "features": [], - "images": [ - "https://images.barcodelookup.com/17022/170222173-1.jpg" - ], - "last_update": "2024-06-01 16:59:56", - "stores": [ - { - "name": "AbeBooks", - "country": "US", - "currency": "USD", - "currency_symbol": "$", - "price": "27.30", - "sale_price": "", - "tax": [], - "link": "https://www.abebooks.com/servlet/BookDetailsPL?bi=31692144717", - "item_group_id": "", - "availability": "", - "condition": "used", - "shipping": [], - "last_update": "2024-05-31 05:32:16" - }, - { - "name": "Wordery (US)", - "country": "GB", - "currency": "GBP", - "currency_symbol": "£", - "price": "28.89", - "sale_price": "", - "tax": [], - "link": "https://wordery.com/building-event-driven-microservices-adam-bellemare-9781492057895", - "item_group_id": "", - "availability": "in stock", - "condition": "new", - "shipping": [], - "last_update": "2021-08-19 21:31:14" - }, - { - "name": "Studibuch Shop DE", - "country": "EU", - "currency": "EUR", - "currency_symbol": "€", - "price": "31.31", - "sale_price": "", - "tax": [], - "link": "https://shop.studibuch.de/9781492057895/building-event-driven-microservices", - "item_group_id": "", - "availability": "in stock", - "condition": "", - "shipping": [], - "last_update": "2024-04-19 16:21:14" - }, - { - "name": "VitalSource", - "country": "AU", - "currency": "AUD", - "currency_symbol": "$", - "price": "34.50", - "sale_price": "", - "tax": [], - "link": "https://www.vitalsource.com/en-uk/products/building-event-driven-microservices-adam-bellemare-v9781492057840?duration=perpetual", - "item_group_id": "", - "availability": "in stock", - "condition": "new", - "shipping": [], - "last_update": "2024-05-06 23:44:20" - }, - { - "name": "Barnes & Noble", - "country": "US", - "currency": "USD", - "currency_symbol": "$", - "price": "36.32", - "sale_price": "", - "tax": [], - "link": "https://www.barnesandnoble.com/w/building-event-driven-microservices-adam-bellemare/1136377083?ean=9781492057895", - "item_group_id": "", - "availability": "", - "condition": "", - "shipping": [], - "last_update": "2021-06-22 04:36:10" - }, - { - "name": "Target", - "country": "US", - "currency": "USD", - "currency_symbol": "$", - "price": "36.49", - "sale_price": "", - "tax": [], - "link": "https://www.target.com/p/building-event-driven-microservices-by-adam-bellemare-paperback/-/A-83017185", - "item_group_id": "", - "availability": "", - "condition": "new", - "shipping": [], - "last_update": "2024-06-01 16:59:56" - }, - { - "name": "Walmart", - "country": "US", - "currency": "USD", - "currency_symbol": "$", - "price": "35.99", - "sale_price": "", - "tax": [], - "link": "https://www.walmart.com/ip/Building-Event-Driven-Microservices-Leveraging-Organizational-Data-at-Scale-Paperback-9781492057895/474384970", - "item_group_id": "", - "availability": "", - "condition": "", - "shipping": [], - "last_update": "2024-02-13 07:28:49" - }, - { - "name": "Hive Books", - "country": "GB", - "currency": "GBP", - "currency_symbol": "£", - "price": "41.35", - "sale_price": "", - "tax": [], - "link": "https://track.flexlinks.com/p.ashx?foc=101&fopid=1199128.171822.156100.57B0.F9670628132FAC0F.9781492057895", - "item_group_id": "", - "availability": "", - "condition": "new", - "shipping": [], - "last_update": "2024-05-19 03:22:49" - }, - { - "name": "Knetbooks.com", - "country": "US", - "currency": "USD", - "currency_symbol": "$", - "price": "44.54", - "sale_price": "", - "tax": [], - "link": "http://www.knetbooks.com/search-results?terms=9781492057895&referrer=KBCJ", - "item_group_id": "", - "availability": "in stock", - "condition": "new", - "shipping": [], - "last_update": "2023-12-13 03:00:01" - }, - { - "name": "eCampus.com", - "country": "US", - "currency": "USD", - "currency_symbol": "$", - "price": "44.99", - "sale_price": "", - "tax": [], - "link": "http://www.ecampus.com/bk_detail.asp?isbn=9781492057895&referrer=CJ", - "item_group_id": "", - "availability": "", - "condition": "new", - "shipping": [], - "last_update": "2023-05-09 22:11:15" - }, - { - "name": "Rarewaves", - "country": "GB", - "currency": "GBP", - "currency_symbol": "£", - "price": "45.32", - "sale_price": "", - "tax": [], - "link": "https://www.rarewaves.com/products/9781492057895-building-event-driven-microser?variant=32564063469665¤cy=GBP&utm_medium=affiliate&utm_source=awin&utm_term=Paperback", - "item_group_id": "", - "availability": "in stock", - "condition": "new", - "shipping": [], - "last_update": "2024-04-18 22:13:09" - }, - { - "name": "BiggerBooks.com", - "country": "US", - "currency": "USD", - "currency_symbol": "$", - "price": "46.34", - "sale_price": "", - "tax": [], - "link": "http://www.biggerbooks.com/bk_detail.asp?isbn=9781492057895&referrer=BBCJ", - "item_group_id": "", - "availability": "in stock", - "condition": "new", - "shipping": [], - "last_update": "2024-04-28 02:44:18" - }, - { - "name": "The Book Depository", - "country": "GB", - "currency": "GBP", - "currency_symbol": "£", - "price": "47.99", - "sale_price": "", - "tax": [], - "link": "https://www.bookdepository.com/Building-Event-Driven-Microservices-Adam-Bellemare/9781492057895?redirected=true&utm_medium=Google&utm_campaign=Base1&utm_source=UK&utm_content=Building-Event-Driven-Microservices&selectCurrency=GBP&w=AFC7AU96Q2NTLHA8VTJV", - "item_group_id": "", - "availability": "", - "condition": "new", - "shipping": [], - "last_update": "2022-10-25 11:46:30" - }, - { - "name": "Foyles for books", - "country": "GB", - "currency": "GBP", - "currency_symbol": "£", - "price": "52.99", - "sale_price": "", - "tax": [], - "link": "https://www.foyles.co.uk/book/building-event-driven-microservices/adam-bellemare//9781492057895", - "item_group_id": "", - "availability": "in stock", - "condition": "new", - "shipping": [], - "last_update": "2023-11-16 12:46:22" - }, - { - "name": "averdo DE", - "country": "EU", - "currency": "EUR", - "currency_symbol": "€", - "price": "53.69", - "sale_price": "", - "tax": [], - "link": "https://shop.averdo.com/product/92797154", - "item_group_id": "", - "availability": "in stock", - "condition": "new", - "shipping": [], - "last_update": "2022-07-16 17:18:02" - }, - { - "name": "Foyles for books (UK) (1414)_Closing", - "country": "GB", - "currency": "GBP", - "currency_symbol": "£", - "price": "52.99", - "sale_price": "", - "tax": [], - "link": "https://www.foyles.co.uk/book/building-event-driven-microservices/adam-bellemare//9781492057895", - "item_group_id": "", - "availability": "in stock", - "condition": "new", - "shipping": [], - "last_update": "2024-04-15 08:28:54" - }, - { - "name": "The Book Depository (US)", - "country": "US", - "currency": "USD", - "currency_symbol": "$", - "price": "59.99", - "sale_price": "", - "tax": [], - "link": "https://www.bookdepository.com/Building-Event-Driven-Microservices-Adam-Bellemare/9781492057895?redirected=true&utm_medium=Google&utm_campaign=Base1&utm_source=US&utm_content=Building-Event-Driven-Microservices&selectCurrency=USD&w=AFCCAU96Q2NTLHA8VT1N", - "item_group_id": "", - "availability": "in stock", - "condition": "new", - "shipping": [], - "last_update": "2022-07-16 05:35:02" - }, - { - "name": "Medimops DE", - "country": "EU", - "currency": "EUR", - "currency_symbol": "€", - "price": "62.00", - "sale_price": "", - "tax": [], - "link": "https://t.neory-tm.net/tm/a/container/redirect/94fca0563d.rd?pubType=!!!publishertagid!!!&touchpoint=!!!awc!!!&pubId=!!!affid!!!&tmrde=", - "item_group_id": "", - "availability": "in stock", - "condition": "", - "shipping": [], - "last_update": "2022-02-28 00:25:52" - }, - { - "name": "Angus & Robertson", - "country": "US", - "currency": "USD", - "currency_symbol": "$", - "price": "78.75", - "sale_price": "", - "tax": [], - "link": "https://www.angusrobertson.com.au/books/building-event-driven-microservices-adam-bellemare/p/9781492057895", - "item_group_id": "", - "availability": "", - "condition": "", - "shipping": [], - "last_update": "2021-06-22 04:28:14" - }, - { - "name": "Indigo Books & Music", - "country": "CA", - "currency": "CAD", - "currency_symbol": "$", - "price": "79.99", - "sale_price": "", - "tax": [], - "link": "https://www.chapters.indigo.ca/en-ca/books/product/9781492057895-item.html", - "item_group_id": "", - "availability": "", - "condition": "new", - "shipping": [], - "last_update": "2023-05-17 08:39:24" - }, - { - "name": "Elefant.ro", - "country": "RO", - "currency": "RON", - "currency_symbol": "L", - "price": "325.99", - "sale_price": "324.99", - "tax": [], - "link": "https://www.elefant.ro/building-event-driven-microservices-leveraging-organizational-data-at-scale-paperback_854bef99-d6a1-425e-b52e-28e72ba06dc7", - "item_group_id": "", - "availability": "in stock", - "condition": "new", - "shipping": [], - "last_update": "2024-04-29 17:01:06" - } - ], - "reviews": [] - } - ] - } - } -} - - - - diff --git a/software/plugins/external-item-search/dev/services/datakick/README.md b/software/plugins/external-item-search/dev/services/mappings/datakick/README.md similarity index 100% rename from software/plugins/external-item-search/dev/services/datakick/README.md rename to software/plugins/external-item-search/dev/services/mappings/datakick/README.md diff --git a/software/plugins/external-item-search/dev/services/mappings/datakick/datakick_barcode.json b/software/plugins/external-item-search/dev/services/mappings/datakick/datakick_barcode.json new file mode 100644 index 0000000000..7dd40e8e81 --- /dev/null +++ b/software/plugins/external-item-search/dev/services/mappings/datakick/datakick_barcode.json @@ -0,0 +1,54 @@ +{ + "request": { + "urlPathPattern": "/datakick/api/items/00888109010058", + "method": "GET" + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "delayDistribution": { + "type": "lognormal", + "median": 7000, + "sigma": 0.4 + }, + "jsonBody": [ + { + "id": 7471, + "user_id": 0, + "gtin14": "00888109010058", + "brand_name": "Cloud", + "name": "- Blackberry peach", + "size": "30ML", + "ingredients": "s", + "serving_size": null, + "serving_per_container": null, + "calories": null, + "fat_calories": null, + "fat": null, + "saturated_fat": null, + "trans_fat": null, + "polyunsaturated_fat": null, + "monounsaturated_fat": null, + "cholesterol": null, + "sodium": null, + "potassium": null, + "carbohydrate": null, + "fiber": null, + "sugar": null, + "protein": null, + "author": null, + "publisher": null, + "pages": null, + "alcohol": null, + "created_at": "2022-04-14T08:12:24.000000Z", + "updated_at": "2024-11-03T19:25:59.000000Z" + } + ] + } +} + + + + diff --git a/software/plugins/external-item-search/dev/services/mappings/datakick/datakick_barcode_not_found.json b/software/plugins/external-item-search/dev/services/mappings/datakick/datakick_barcode_not_found.json new file mode 100644 index 0000000000..14f98ed207 --- /dev/null +++ b/software/plugins/external-item-search/dev/services/mappings/datakick/datakick_barcode_not_found.json @@ -0,0 +1,22 @@ +{ + "request": { + "urlPathPattern": "/datakick/api/items/(?!00888109010058(?=/|$))$", + "method": "GET" + }, + "response": { + "status": 200, + "delayDistribution": { + "type": "lognormal", + "median": 5000, + "sigma": 0.4 + }, + "headers": { + "Content-Type": "application/json" + }, + "jsonBody": [] + } +} + + + + diff --git a/software/plugins/external-item-search/dev/services/rebrickable/README.md b/software/plugins/external-item-search/dev/services/mappings/rebrickable/README.md similarity index 100% rename from software/plugins/external-item-search/dev/services/rebrickable/README.md rename to software/plugins/external-item-search/dev/services/mappings/rebrickable/README.md diff --git a/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part.json b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part.json new file mode 100644 index 0000000000..e1d781bf94 --- /dev/null +++ b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part.json @@ -0,0 +1,56 @@ +{ + "request": { + "urlPathPattern": "/rebrickable/api/v3/lego/parts/012345/", + "method": "GET", + "headers": { + "Authorization": { + "equalTo": "key devKey" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "delayDistribution": { + "type": "lognormal", + "median": 5000, + "sigma": 0.4 + }, + "jsonBody": { + "part_num": "{{request.pathSegments.[5]}}", + "name": "Technic Turntable 5 x 5 Top with 5 Pin Holes", + "part_cat_id": 26, + "year_from": 2022, + "year_to": 2022, + "part_url": "https://rebrickable.com/parts/01936/technic-turntable-5-x-5-top-with-5-pin-holes/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6399646.jpg", + "prints": [], + "molds": [], + "alternates": [], + "external_ids": { + "BrickLink": [ + "1936" + ], + "BrickOwl": [ + "1125979" + ], + "Brickset": [ + "1936" + ], + "LDraw": [ + "1936" + ], + "LEGO": [ + "1936" + ] + }, + "print_of": null + } + } +} + + + + diff --git a/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part_bad_key.json b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part_bad_key.json new file mode 100644 index 0000000000..b373639268 --- /dev/null +++ b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part_bad_key.json @@ -0,0 +1,29 @@ +{ + "request": { + "urlPathPattern": "/rebrickable/api/v3/lego/parts/(.*)/", + "method": "GET", + "headers": { + "Authorization": { + "equalTo": "key devKeyBad" + } + } + }, + "response": { + "status": 401, + "delayDistribution": { + "type": "lognormal", + "median": 100, + "sigma": 0.4 + }, + "headers": { + "Content-Type": "application/json" + }, + "jsonBody": { + "detail": "Invalid Token." + } + } +} + + + + diff --git a/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part_not_found.json b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part_not_found.json new file mode 100644 index 0000000000..40de39b7ce --- /dev/null +++ b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part_not_found.json @@ -0,0 +1,29 @@ +{ + "request": { + "urlPathPattern": "/rebrickable/api/v3/lego/parts/(?!012345(?=/|$))([^/]+)/$", + "method": "GET", + "headers": { + "Authorization": { + "equalTo": "key devKey" + } + } + }, + "response": { + "status": 404, + "delayDistribution": { + "type": "lognormal", + "median": 500, + "sigma": 0.4 + }, + "headers": { + "Content-Type": "application/json" + }, + "jsonBody": { + "detail": "No Part matches the given query." + } + } +} + + + + diff --git a/software/plugins/external-item-search/dev/services/mappings/upcitemdb_barcode.json b/software/plugins/external-item-search/dev/services/mappings/upcitemdb_barcode.json deleted file mode 100644 index 0ca631b3d8..0000000000 --- a/software/plugins/external-item-search/dev/services/mappings/upcitemdb_barcode.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "request": { - "urlPathPattern": "/upcitemdb/prod/v1/lookup", - "method": "GET" - }, - "response": { - "status": 200, - "headers": { - "Content-Type": "application/json" - }, - "jsonBody": { - "code" : "OK", - "total" : 0, - "offset" : 0, - "items" : [ - ] - } - } -} - - - - diff --git a/software/plugins/external-item-search/dev/services/rebrickable/rebrickable_part.json b/software/plugins/external-item-search/dev/services/rebrickable/rebrickable_part.json deleted file mode 100644 index 9273215512..0000000000 --- a/software/plugins/external-item-search/dev/services/rebrickable/rebrickable_part.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "request": { - "urlPathPattern": "/rebrickable/api/v3/lego/parts/.*", - "method": "GET" - }, - "response": { - "status": 200, - "headers": { - "Content-Type": "application/json" - }, - "jsonBody": { - "part_num": "{{request.pathSegments.[5]}}", - "name": "Technic Turntable 5 x 5 Top with 5 Pin Holes", - "part_cat_id": 26, - "year_from": 2022, - "year_to": 2022, - "part_url": "https://rebrickable.com/parts/01936/technic-turntable-5-x-5-top-with-5-pin-holes/", - "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6399646.jpg", - "prints": [], - "molds": [], - "alternates": [], - "external_ids": { - "BrickLink": [ - "1936" - ], - "BrickOwl": [ - "1125979" - ], - "Brickset": [ - "1936" - ], - "LDraw": [ - "1936" - ], - "LEGO": [ - "1936" - ] - }, - "print_of": null - } - } -} - - - - diff --git a/software/plugins/external-item-search/dev/services/wm_settings.json b/software/plugins/external-item-search/dev/services/wm_settings.json index 30b586a703..72645a5d58 100644 --- a/software/plugins/external-item-search/dev/services/wm_settings.json +++ b/software/plugins/external-item-search/dev/services/wm_settings.json @@ -1,7 +1,7 @@ { "delayDistribution": { "type": "lognormal", - "median": 80, + "median": 5000, "sigma": 0.4 } } \ No newline at end of file diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java index 05c2367d25..d07677b343 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java @@ -24,6 +24,7 @@ import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemSearch; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResults; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ResultType; import tech.ebp.oqm.plugin.extItemSearch.service.ExtItemLookupService; import java.net.MalformedURLException; @@ -80,7 +81,7 @@ public Response allProviderInfo() { @PermitAll @Produces(MediaType.APPLICATION_JSON) public Multi search(@Valid @BeanParam ExtItemSearch search) { - return this.productLookupService.search(search); + return this.productLookupService.search(search).filter(r -> !r.getType().equals(ResultType.NO_RESULTS)); } } \ No newline at end of file diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupResult.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupResult.java index 9b6f340031..146f4f34d6 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupResult.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupResult.java @@ -46,9 +46,11 @@ public ResultType getType() { @lombok.Builder.Default private String description = ""; - private BigDecimal price; + @lombok.Builder.Default + private Map prices = new HashMap<>(); - private String barcode; + @lombok.Builder.Default + private Map identifiers = new HashMap<>(); @NonNull @NotNull @@ -60,6 +62,11 @@ public ResultType getType() { @lombok.Builder.Default private List<@NotNull @NonNull @NotBlank String> images = new ArrayList<>(); + @NonNull + @NotNull + @lombok.Builder.Default + private Map links = new HashMap<>(); + public ExtItemLookupResult addAttIfNotBlank(String key, String val) { if ( key != null && !key.isBlank() && diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/LookupResult.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/LookupResult.java index e1039b03ff..75fc4ea2ca 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/LookupResult.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/LookupResult.java @@ -9,6 +9,7 @@ import lombok.NoArgsConstructor; import lombok.NonNull; import lombok.experimental.SuperBuilder; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupType; @Data @AllArgsConstructor @@ -20,7 +21,8 @@ ) @JsonSubTypes({ @JsonSubTypes.Type(value = ExtItemLookupResult.class, name = "SUCCESS"), - @JsonSubTypes.Type(value = ExtItemLookupErrResult.class, name = "ERROR") + @JsonSubTypes.Type(value = ExtItemLookupErrResult.class, name = "ERROR"), + @JsonSubTypes.Type(value = LookupResultNoResults.class, name = "NO_RESULTS") }) public abstract class LookupResult { @@ -30,4 +32,8 @@ public abstract class LookupResult { @NotNull @NotBlank private String source; + + @NonNull + @NotNull + private LookupType lookupType; } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/LookupResultNoResults.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/LookupResultNoResults.java new file mode 100644 index 0000000000..285009d046 --- /dev/null +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/LookupResultNoResults.java @@ -0,0 +1,36 @@ +package tech.ebp.oqm.plugin.extItemSearch.model.lookupResult; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * An individual result from external sources. + */ +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Data +@AllArgsConstructor +@NoArgsConstructor +@SuperBuilder +public class LookupResultNoResults extends LookupResult { + + @Override + public ResultType getType() { + return ResultType.NO_RESULTS; + } + + private String detail; + +} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ResultType.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ResultType.java index e3b0eaf585..33c8bba2f7 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ResultType.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ResultType.java @@ -2,5 +2,6 @@ public enum ResultType { SUCCESS, - ERROR + ERROR, + NO_RESULTS } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/ItemSearchService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/ItemSearchService.java index 2e024f86b0..f07ac489a8 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/ItemSearchService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/ItemSearchService.java @@ -2,6 +2,7 @@ import io.smallrye.mutiny.Multi; +import jakarta.ws.rs.WebApplicationException; import lombok.extern.slf4j.Slf4j; import org.jboss.resteasy.reactive.ClientWebApplicationException; import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; @@ -67,22 +68,46 @@ public Optional> search(LookupType type, String search) { }; } + protected Optional handleClientError( + LookupType type, + ClientWebApplicationException e + ) { + //noting to do by default. + return Optional.empty(); + } - protected ExtItemLookupErrResult handleError(Throwable error) { + protected LookupResult handleError(LookupType type, Throwable error) { log.warn("Error searching for ext items: {}", error.getMessage(), error); - ExtItemLookupErrResult.Builder builder = ExtItemLookupErrResult.builder(); + ExtItemLookupErrResult.Builder builder = this.setupResponseBuilder(ExtItemLookupErrResult.builder(), type); + + builder.lookupType(type); builder.source(this.getProviderInfo().getId()); builder.errMessage(error.getMessage()); - if(error instanceof ClientWebApplicationException) { - builder.errCode(((ClientWebApplicationException) error).getResponse().getStatus()); - builder.errMessage(((ClientWebApplicationException) error).getResponse().getStatusInfo().getReasonPhrase()); + if(error instanceof WebApplicationException) { + builder.errCode(((WebApplicationException) error).getResponse().getStatus()); + builder.errMessage(((WebApplicationException) error).getResponse().getStatusInfo().getReasonPhrase()); + + if(error instanceof ClientWebApplicationException){ + Optional handled = this.handleClientError(type, (ClientWebApplicationException) error); + if(handled.isPresent()){ + return handled.get(); + } + } } return builder.build(); } - protected Collection handleErrorRetCollection(Throwable error) { - return List.of(this.handleError(error)); + + protected Collection handleErrorRetCollection(LookupType type, Throwable error) { + return List.of(this.handleError(type, error)); + } + + protected > T setupResponseBuilder(T builder, LookupType type) { + builder.lookupType(type); + builder.source(this.getProviderInfo().getId()); + + return builder; } } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DataKickLookupClient.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DataKickLookupClient.java index 93e1882ea2..3919f27bea 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DataKickLookupClient.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DataKickLookupClient.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import io.opentelemetry.instrumentation.annotations.WithSpan; +import io.quarkus.cache.CacheResult; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; import jakarta.ws.rs.GET; @@ -20,5 +21,6 @@ public interface DataKickLookupClient { @WithSpan @GET @Path("{upc}") + @CacheResult(cacheName = "datakick-barcode-search") Uni getFromUpcCode(@PathParam("upc") String barcode); } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java index 31584feabc..e8c3bdae57 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java @@ -18,6 +18,7 @@ import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResult; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupType; import java.net.URI; import java.util.*; @@ -57,11 +58,6 @@ public DatakickService( .build(); } - @CacheResult(cacheName = "datakick-barcode-search") - public Uni performBarcodeSearch(String barcode) { - return this.dataKickLookupClient.getFromUpcCode(barcode); - } - @Override public boolean isEnabled() { return this.getProviderInfo().isEnabled(); @@ -75,12 +71,12 @@ public boolean isEnabled() { * @return */ @WithSpan - public Collection jsonNodeToSearchResults(ArrayNode results) { + public Collection jsonNodeToSearchResults(LookupType type, ArrayNode results) { log.debug("Data from Datakick: {}", results); List resultsList = new ArrayList<>(results.size()); - for(JsonNode curResult : results) { + for (JsonNode curResult : results) { ObjectNode curResultJson = (ObjectNode) curResult; String brandName = ""; String name = ""; @@ -113,13 +109,14 @@ public Collection jsonNodeToSearchResults(ArrayNode results) { } resultsList.add(ExtItemLookupResult - .builder() - .source(this.getProviderInfo().getDisplayName()) - .name(name) - .brand(brandName) - .unifiedName(name) - .attributes(attributes) - .build() + .builder() + .lookupType(type) + .source(this.getProviderInfo().getDisplayName()) + .name(name) + .brand(brandName) + .unifiedName(name) + .attributes(attributes) + .build() ); } return resultsList; @@ -128,11 +125,11 @@ public Collection jsonNodeToSearchResults(ArrayNode results) { @Override public Optional> searchBarcode(String search) { return Optional.of( - this.performBarcodeSearch(search) - .map(this::jsonNodeToSearchResults) - .onFailure().recoverWithItem(this::handleErrorRetCollection) + this.dataKickLookupClient.getFromUpcCode(search) + .map(results->this.jsonNodeToSearchResults(LookupType.BARCODE, results)) + .onFailure().recoverWithItem(e -> this.handleErrorRetCollection(LookupType.BARCODE, e)) - .onItem().transformToMulti(collection -> + .onItem().transformToMulti(collection-> Multi.createFrom().iterable(collection) ) ); diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableLookupClient.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableLookupClient.java index e3c70ae0bf..d6aceed1d3 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableLookupClient.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableLookupClient.java @@ -3,8 +3,10 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import io.opentelemetry.instrumentation.annotations.WithSpan; +import io.quarkus.cache.CacheResult; import io.smallrye.mutiny.Uni; import jakarta.ws.rs.GET; +import jakarta.ws.rs.HeaderParam; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; import jakarta.ws.rs.QueryParam; @@ -19,5 +21,6 @@ public interface RebrickableLookupClient { @GET @Path("parts/{partNo}/") @WithSpan - Uni getFromPartNum(@QueryParam("key") String apiKey, @PathParam("partNo") String partNumber); + @CacheResult(cacheName = "rebrickable-part-num-search") + Uni getFromPartNum(@HeaderParam("Authorization") String apiKey, @PathParam("partNo") String partNumber); } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java index e72e0aefe7..709272ec42 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java @@ -1,6 +1,8 @@ package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.rebrickable; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import io.opentelemetry.instrumentation.annotations.WithSpan; import io.quarkus.cache.CacheResult; @@ -13,13 +15,18 @@ import lombok.extern.slf4j.Slf4j; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.jboss.resteasy.reactive.ClientWebApplicationException; import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResult; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResultNoResults; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.ItemKind; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupType; +import java.io.InputStream; import java.net.URI; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -34,12 +41,14 @@ @Slf4j @NoArgsConstructor public class RebrickableService extends ItemSearchService { + private static final String BRAND = "LEGO"; @Getter ExtItemLookupProviderInfo providerInfo; RebrickableLookupClient rebrickableLookupClient; private String apiKey; + private ObjectMapper objectMapper; @Inject public RebrickableService( @@ -48,10 +57,12 @@ public RebrickableService( @ConfigProperty(name = "productLookup.providers.rebrickable.enabled", defaultValue = "false") boolean enabled, @ConfigProperty(name = "productLookup.providers.rebrickable.apiKey", defaultValue = "") - String apiKey + String apiKey, + ObjectMapper objectMapper ) { this.rebrickableLookupClient = rebrickableLookupClient; this.apiKey = apiKey; + this.objectMapper = objectMapper; ExtItemLookupProviderInfo.Builder infoBuilder = ExtItemLookupProviderInfo .builder() @@ -82,50 +93,112 @@ public boolean isEnabled() { } @WithSpan - public LookupResult jsonNodeToSearchResults(ObjectNode results) { + public LookupResult jsonNodeToSearchResults(LookupType type, ObjectNode results) { log.info("Search result: {}", results); ExtItemLookupResult.Builder resultBuilder = ExtItemLookupResult.builder() + .lookupType(type) .source(this.getProviderInfo().getDisplayName()) .brand(BRAND); + List images = new ArrayList<>(); + Map links = new HashMap<>(); + Map identifiers = new HashMap<>(); Map attributes = new HashMap<>(); for (Map.Entry curField : results.properties()) { String curFieldName = curField.getKey(); - String curFieldVal = curField.getValue().asText(); - - //TODO:: handle images + JsonNode curFieldVal = curField.getValue(); - if (curField.getValue().isNull() || curFieldVal == null || curFieldVal.isBlank()) { + if (curField.getValue().isNull() || curFieldVal == null) { + continue; + } + if (curFieldVal.isTextual() && curFieldVal.asText().isBlank()) { + continue; + } + if (curFieldVal.isArray() && curFieldVal.isEmpty()) { + continue; + } + if (curFieldVal.isObject() && curFieldVal.isEmpty()) { continue; } switch (curFieldName) { case "name": - resultBuilder.name(curFieldVal); - resultBuilder.unifiedName(curFieldVal); + resultBuilder.name(curFieldVal.asText()); + resultBuilder.unifiedName(curFieldVal.asText()); + break; + case "part_num": + identifiers.put("legoPartNum", curFieldVal.asText()); + break; + case "part_img_url": + images.add(curFieldVal.asText()); + break; + case "part_url": + links.put("rebrickable", curFieldVal.asText()); + break; + case "external_ids": + for (Map.Entry curId : curFieldVal.properties()) { + ArrayNode curIdArr = (ArrayNode) curId.getValue(); + if (curIdArr.isEmpty()) { + continue; + } + if (curIdArr.size() == 1) { + identifiers.put(curId.getKey(), curIdArr.get(0).asText()); + } else { + //iterate over the array and add each value to the identifiers map + int i = 0; + for (JsonNode curIdNode : curIdArr) { + identifiers.put(curId.getKey() + "-" + i++, curIdNode.asText()); + } + } + } break; default: - attributes.put(curFieldName, curFieldVal); + if (curFieldVal.isValueNode()) { + attributes.put(curFieldName, curFieldVal.asText()); + } } } + resultBuilder.identifiers(identifiers); + resultBuilder.images(images); + resultBuilder.links(links); resultBuilder.attributes(attributes); return resultBuilder.build(); } - @CacheResult(cacheName = "rebrickable-part-num-search") - public Uni performPartNoSearch(String partNum) { - return this.rebrickableLookupClient.getFromPartNum(this.apiKey, partNum); + protected String getApiKey() { + return "key " + this.apiKey; } @Override public Optional> searchPartNum(String partNum) { return Optional.of( - this.performPartNoSearch(partNum) - .map(this::jsonNodeToSearchResults) + this.rebrickableLookupClient.getFromPartNum(this.getApiKey(), partNum) + .map(result->this.jsonNodeToSearchResults(LookupType.PART_NUM, result)) + .onFailure().recoverWithItem(e->this.handleError(LookupType.PART_NUM, e)) .toMulti() ); } + + @Override + protected Optional handleClientError(LookupType type, ClientWebApplicationException e) { + if (e.getResponse().getStatus() == 404) { + try { + ObjectNode errorDeets = (ObjectNode) this.objectMapper.readTree((InputStream) e.getResponse().getEntity()); + + if (errorDeets.get("detail").asText().equals("No Part matches the given query.")) { + return Optional.of( + this.setupResponseBuilder(LookupResultNoResults.builder(), type) + .detail("No Part matches the given query.") + .build() + ); + } + } catch(Throwable e2) { + log.warn("Error parsing error response from Rebrickable: {}", e2.getMessage(), e2); + } + } + return Optional.empty(); + } } From fdc3910eab5a5bf32b89b15928e94e7255c858b5 Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Wed, 22 Apr 2026 00:58:49 -0400 Subject: [PATCH 10/38] Plugin - Ext Item Search - Adding back in barcode lookup --- .../old/mappings/barcodelookup_barcode.json | 372 ------------------ .../services/mappings/barcodelookup/README.md | 4 + .../barcodelookup/barcodelookup_barcode.json | 122 ++++++ .../barcodelookup_barcode_bad_key.json | 29 ++ .../barcodelookup_barcode_no_results.json | 29 ++ .../lookupResult/ExtItemLookupResult.java | 2 - .../service/ExtItemLookupService.java | 5 +- .../barcodeLookup/BarcodeLookupClient.java | 8 +- .../barcodeLookup/BarcodeLookupService.java | 341 +++++++++------- .../providers/dataKick/DatakickService.java | 16 +- .../rebrickable/RebrickableService.java | 17 +- .../searchServices/utils/LookupTypes.java | 7 + .../utils/ResultMappingUtils.java | 22 ++ .../src/main/resources/application.yml | 6 - 14 files changed, 431 insertions(+), 549 deletions(-) delete mode 100644 software/plugins/external-item-search/dev/old/mappings/barcodelookup_barcode.json create mode 100644 software/plugins/external-item-search/dev/services/mappings/barcodelookup/README.md create mode 100644 software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_barcode.json create mode 100644 software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_barcode_bad_key.json create mode 100644 software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_barcode_no_results.json create mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupTypes.java create mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/ResultMappingUtils.java diff --git a/software/plugins/external-item-search/dev/old/mappings/barcodelookup_barcode.json b/software/plugins/external-item-search/dev/old/mappings/barcodelookup_barcode.json deleted file mode 100644 index 4abe34fcea..0000000000 --- a/software/plugins/external-item-search/dev/old/mappings/barcodelookup_barcode.json +++ /dev/null @@ -1,372 +0,0 @@ -{ - "request": { - "urlPathPattern": "/v3/products", - "method": "GET" - }, - "response": { - "status": 200, - "headers": { - "Content-Type": "application/json" - }, - "jsonBody": { - "products": [ - { - "barcode_number": "9781492057895", - "barcode_formats": "ISBN-10 1492057894, ISBN-13 9781492057895", - "mpn": "83017185", - "model": "", - "asin": "", - "title": "Building Event-Driven Microservices - by Adam Bellemare (Paperback)", - "category": "Media > Books", - "manufacturer": "O'Reilly Media", - "brand": "O'Reilly Media", - "contributors": [], - "age_group": "", - "ingredients": "", - "nutrition_facts": "", - "energy_efficiency_class": "", - "color": "", - "gender": "", - "material": "", - "pattern": "", - "format": "", - "multipack": "", - "size": "0.68 x 9.19 x 1", - "length": "", - "width": "", - "height": "", - "weight": "", - "release_date": "", - "description": "Building Event-Driven Microservices - by Adam Bellemare (Paperback).", - "features": [], - "images": [ - "https://images.barcodelookup.com/17022/170222173-1.jpg" - ], - "last_update": "2024-06-01 16:59:56", - "stores": [ - { - "name": "AbeBooks", - "country": "US", - "currency": "USD", - "currency_symbol": "$", - "price": "27.30", - "sale_price": "", - "tax": [], - "link": "https://www.abebooks.com/servlet/BookDetailsPL?bi=31692144717", - "item_group_id": "", - "availability": "", - "condition": "used", - "shipping": [], - "last_update": "2024-05-31 05:32:16" - }, - { - "name": "Wordery (US)", - "country": "GB", - "currency": "GBP", - "currency_symbol": "£", - "price": "28.89", - "sale_price": "", - "tax": [], - "link": "https://wordery.com/building-event-driven-microservices-adam-bellemare-9781492057895", - "item_group_id": "", - "availability": "in stock", - "condition": "new", - "shipping": [], - "last_update": "2021-08-19 21:31:14" - }, - { - "name": "Studibuch Shop DE", - "country": "EU", - "currency": "EUR", - "currency_symbol": "€", - "price": "31.31", - "sale_price": "", - "tax": [], - "link": "https://shop.studibuch.de/9781492057895/building-event-driven-microservices", - "item_group_id": "", - "availability": "in stock", - "condition": "", - "shipping": [], - "last_update": "2024-04-19 16:21:14" - }, - { - "name": "VitalSource", - "country": "AU", - "currency": "AUD", - "currency_symbol": "$", - "price": "34.50", - "sale_price": "", - "tax": [], - "link": "https://www.vitalsource.com/en-uk/products/building-event-driven-microservices-adam-bellemare-v9781492057840?duration=perpetual", - "item_group_id": "", - "availability": "in stock", - "condition": "new", - "shipping": [], - "last_update": "2024-05-06 23:44:20" - }, - { - "name": "Barnes & Noble", - "country": "US", - "currency": "USD", - "currency_symbol": "$", - "price": "36.32", - "sale_price": "", - "tax": [], - "link": "https://www.barnesandnoble.com/w/building-event-driven-microservices-adam-bellemare/1136377083?ean=9781492057895", - "item_group_id": "", - "availability": "", - "condition": "", - "shipping": [], - "last_update": "2021-06-22 04:36:10" - }, - { - "name": "Target", - "country": "US", - "currency": "USD", - "currency_symbol": "$", - "price": "36.49", - "sale_price": "", - "tax": [], - "link": "https://www.target.com/p/building-event-driven-microservices-by-adam-bellemare-paperback/-/A-83017185", - "item_group_id": "", - "availability": "", - "condition": "new", - "shipping": [], - "last_update": "2024-06-01 16:59:56" - }, - { - "name": "Walmart", - "country": "US", - "currency": "USD", - "currency_symbol": "$", - "price": "35.99", - "sale_price": "", - "tax": [], - "link": "https://www.walmart.com/ip/Building-Event-Driven-Microservices-Leveraging-Organizational-Data-at-Scale-Paperback-9781492057895/474384970", - "item_group_id": "", - "availability": "", - "condition": "", - "shipping": [], - "last_update": "2024-02-13 07:28:49" - }, - { - "name": "Hive Books", - "country": "GB", - "currency": "GBP", - "currency_symbol": "£", - "price": "41.35", - "sale_price": "", - "tax": [], - "link": "https://track.flexlinks.com/p.ashx?foc=101&fopid=1199128.171822.156100.57B0.F9670628132FAC0F.9781492057895", - "item_group_id": "", - "availability": "", - "condition": "new", - "shipping": [], - "last_update": "2024-05-19 03:22:49" - }, - { - "name": "Knetbooks.com", - "country": "US", - "currency": "USD", - "currency_symbol": "$", - "price": "44.54", - "sale_price": "", - "tax": [], - "link": "http://www.knetbooks.com/search-results?terms=9781492057895&referrer=KBCJ", - "item_group_id": "", - "availability": "in stock", - "condition": "new", - "shipping": [], - "last_update": "2023-12-13 03:00:01" - }, - { - "name": "eCampus.com", - "country": "US", - "currency": "USD", - "currency_symbol": "$", - "price": "44.99", - "sale_price": "", - "tax": [], - "link": "http://www.ecampus.com/bk_detail.asp?isbn=9781492057895&referrer=CJ", - "item_group_id": "", - "availability": "", - "condition": "new", - "shipping": [], - "last_update": "2023-05-09 22:11:15" - }, - { - "name": "Rarewaves", - "country": "GB", - "currency": "GBP", - "currency_symbol": "£", - "price": "45.32", - "sale_price": "", - "tax": [], - "link": "https://www.rarewaves.com/products/9781492057895-building-event-driven-microser?variant=32564063469665¤cy=GBP&utm_medium=affiliate&utm_source=awin&utm_term=Paperback", - "item_group_id": "", - "availability": "in stock", - "condition": "new", - "shipping": [], - "last_update": "2024-04-18 22:13:09" - }, - { - "name": "BiggerBooks.com", - "country": "US", - "currency": "USD", - "currency_symbol": "$", - "price": "46.34", - "sale_price": "", - "tax": [], - "link": "http://www.biggerbooks.com/bk_detail.asp?isbn=9781492057895&referrer=BBCJ", - "item_group_id": "", - "availability": "in stock", - "condition": "new", - "shipping": [], - "last_update": "2024-04-28 02:44:18" - }, - { - "name": "The Book Depository", - "country": "GB", - "currency": "GBP", - "currency_symbol": "£", - "price": "47.99", - "sale_price": "", - "tax": [], - "link": "https://www.bookdepository.com/Building-Event-Driven-Microservices-Adam-Bellemare/9781492057895?redirected=true&utm_medium=Google&utm_campaign=Base1&utm_source=UK&utm_content=Building-Event-Driven-Microservices&selectCurrency=GBP&w=AFC7AU96Q2NTLHA8VTJV", - "item_group_id": "", - "availability": "", - "condition": "new", - "shipping": [], - "last_update": "2022-10-25 11:46:30" - }, - { - "name": "Foyles for books", - "country": "GB", - "currency": "GBP", - "currency_symbol": "£", - "price": "52.99", - "sale_price": "", - "tax": [], - "link": "https://www.foyles.co.uk/book/building-event-driven-microservices/adam-bellemare//9781492057895", - "item_group_id": "", - "availability": "in stock", - "condition": "new", - "shipping": [], - "last_update": "2023-11-16 12:46:22" - }, - { - "name": "averdo DE", - "country": "EU", - "currency": "EUR", - "currency_symbol": "€", - "price": "53.69", - "sale_price": "", - "tax": [], - "link": "https://shop.averdo.com/product/92797154", - "item_group_id": "", - "availability": "in stock", - "condition": "new", - "shipping": [], - "last_update": "2022-07-16 17:18:02" - }, - { - "name": "Foyles for books (UK) (1414)_Closing", - "country": "GB", - "currency": "GBP", - "currency_symbol": "£", - "price": "52.99", - "sale_price": "", - "tax": [], - "link": "https://www.foyles.co.uk/book/building-event-driven-microservices/adam-bellemare//9781492057895", - "item_group_id": "", - "availability": "in stock", - "condition": "new", - "shipping": [], - "last_update": "2024-04-15 08:28:54" - }, - { - "name": "The Book Depository (US)", - "country": "US", - "currency": "USD", - "currency_symbol": "$", - "price": "59.99", - "sale_price": "", - "tax": [], - "link": "https://www.bookdepository.com/Building-Event-Driven-Microservices-Adam-Bellemare/9781492057895?redirected=true&utm_medium=Google&utm_campaign=Base1&utm_source=US&utm_content=Building-Event-Driven-Microservices&selectCurrency=USD&w=AFCCAU96Q2NTLHA8VT1N", - "item_group_id": "", - "availability": "in stock", - "condition": "new", - "shipping": [], - "last_update": "2022-07-16 05:35:02" - }, - { - "name": "Medimops DE", - "country": "EU", - "currency": "EUR", - "currency_symbol": "€", - "price": "62.00", - "sale_price": "", - "tax": [], - "link": "https://t.neory-tm.net/tm/a/container/redirect/94fca0563d.rd?pubType=!!!publishertagid!!!&touchpoint=!!!awc!!!&pubId=!!!affid!!!&tmrde=", - "item_group_id": "", - "availability": "in stock", - "condition": "", - "shipping": [], - "last_update": "2022-02-28 00:25:52" - }, - { - "name": "Angus & Robertson", - "country": "US", - "currency": "USD", - "currency_symbol": "$", - "price": "78.75", - "sale_price": "", - "tax": [], - "link": "https://www.angusrobertson.com.au/books/building-event-driven-microservices-adam-bellemare/p/9781492057895", - "item_group_id": "", - "availability": "", - "condition": "", - "shipping": [], - "last_update": "2021-06-22 04:28:14" - }, - { - "name": "Indigo Books & Music", - "country": "CA", - "currency": "CAD", - "currency_symbol": "$", - "price": "79.99", - "sale_price": "", - "tax": [], - "link": "https://www.chapters.indigo.ca/en-ca/books/product/9781492057895-item.html", - "item_group_id": "", - "availability": "", - "condition": "new", - "shipping": [], - "last_update": "2023-05-17 08:39:24" - }, - { - "name": "Elefant.ro", - "country": "RO", - "currency": "RON", - "currency_symbol": "L", - "price": "325.99", - "sale_price": "324.99", - "tax": [], - "link": "https://www.elefant.ro/building-event-driven-microservices-leveraging-organizational-data-at-scale-paperback_854bef99-d6a1-425e-b52e-28e72ba06dc7", - "item_group_id": "", - "availability": "in stock", - "condition": "new", - "shipping": [], - "last_update": "2024-04-29 17:01:06" - } - ], - "reviews": [] - } - ] - } - } -} - - - - diff --git a/software/plugins/external-item-search/dev/services/mappings/barcodelookup/README.md b/software/plugins/external-item-search/dev/services/mappings/barcodelookup/README.md new file mode 100644 index 0000000000..501b5d8c21 --- /dev/null +++ b/software/plugins/external-item-search/dev/services/mappings/barcodelookup/README.md @@ -0,0 +1,4 @@ +# BarcodeLookup + + - https://www.barcodelookup.com/api + - https://www.barcodelookup.com/api-documentation \ No newline at end of file diff --git a/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_barcode.json b/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_barcode.json new file mode 100644 index 0000000000..084b6cb774 --- /dev/null +++ b/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_barcode.json @@ -0,0 +1,122 @@ +{ + "request": { + "urlPathPattern": "/barcodelookup/v3/products", + "method": "GET", + "queryParameters": { + "key": { + "equalTo": "devKey" + }, + "barcode": { + "equalTo": "886736874135" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "delayDistribution": { + "type": "lognormal", + "median": 3000, + "sigma": 0.4 + }, + "jsonBody": { + "products": [ + { + "barcode_number": "886736874135", + "barcode_formats": "UPC-A 886736874135, EAN-13 0886736874135", + "mpn": "CE-XLR3200", + "model": "XLR", + "asin": "B01KUHG2G8", + "title": "Nike Red Running Shoes - Size 10", + "category": "Media > Books > Print Books", + "manufacturer": "Xerox", + "brand": "Xerox", + "contributors": [ + { + "role": "author", + "name": "Blake, Quentin" + }, + { + "role": "publisher", + "name": "Greenwillow Books" + } + ], + "age_group": "adult", + "ingredients": "Organic Tapioca Syrup, Organic Dried Cane Syrup, Natural Flavor.", + "nutrition_facts": "Protein 0 G, Total lipid (fat) 0 G, Energy 300 KCAL, Sugars, total including NLEA 40 G.", + "energy_efficiency_class": "A+ (A+++ to D)", + "color": "blue", + "gender": "female", + "material": "cloth", + "pattern": "checkered", + "format": "DVD", + "multipack": "8", + "size": "7 US", + "length": "45 in", + "width": "30 in", + "height": "22 in", + "weight": "7 lb", + "release_date": "2003-07-28", + "description": "One of a kind, Nike Red Running Shoes that are great for walking, running and sports.", + "features": [ + "Rugged construction", + "Convenient carrying case", + "5 year warranty" + ], + "images": [ + "https://images.barcodelookup.com/5219/52194594-1.jpg", + "https://images.barcodelookup.com/5219/52194594-2.jpg", + "https://images.barcodelookup.com/5219/52194594-3.jpg" + ], + "last_update": "2022-03-03 20:28:19", + "stores": [ + { + "name": "Newegg.com", + "country": "US", + "currency": "USD", + "currency_symbol": "$", + "price": "41.38", + "sale_price": "35.99", + "tax": [ + { + "country": "US", + "region": "US", + "rate": "5.00", + "tax_ship": "no" + } + ], + "link": "https://www.newegg.com/product-link", + "item_group_id": "AB-4312", + "availability": "in stock", + "condition": "new", + "shipping": [ + { + "country": "US", + "region": "US", + "service": "Standard", + "price": "8.49 USD" + } + ], + "last_update": "2021-05-19 09:07:42" + } + ], + "reviews": [ + { + "name": "Josh Keller", + "rating": "5", + "title": "Love these shoes!", + "review": "A stylish and great fitting shoe for walking and running.", + "date": "2015-03-19 21:48:03" + } + ] + } + ] + } + } +} + + + + diff --git a/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_barcode_bad_key.json b/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_barcode_bad_key.json new file mode 100644 index 0000000000..271b8b7642 --- /dev/null +++ b/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_barcode_bad_key.json @@ -0,0 +1,29 @@ +{ + "request": { + "urlPathPattern": "/barcodelookup/v3/products", + "method": "GET", + "queryParameters": { + "key": { + "equalTo": "devKeyBad" + }, + "barcode": { + "matches": ".*" + } + } + }, + "response": { + "status": 403, + "headers": { + "Content-Type": "application/json" + }, + "delayDistribution": { + "type": "lognormal", + "median": 500, + "sigma": 0.4 + } + } +} + + + + diff --git a/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_barcode_no_results.json b/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_barcode_no_results.json new file mode 100644 index 0000000000..0380a3a8a6 --- /dev/null +++ b/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_barcode_no_results.json @@ -0,0 +1,29 @@ +{ + "request": { + "urlPathPattern": "/barcodelookup/v3/products", + "method": "GET", + "queryParameters": { + "key": { + "equalTo": "devKey" + }, + "barcode": { + "matches": "^((?!886736874135).)*$" + } + } + }, + "response": { + "status": 404, + "headers": { + "Content-Type": "application/json" + }, + "delayDistribution": { + "type": "lognormal", + "median": 3000, + "sigma": 0.4 + } + } +} + + + + diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupResult.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupResult.java index 146f4f34d6..e765d5a13c 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupResult.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupResult.java @@ -32,8 +32,6 @@ public ResultType getType() { return ResultType.SUCCESS; } - private String brand; - private String name; @NonNull diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/ExtItemLookupService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/ExtItemLookupService.java index 02108d99c6..ddda9abfd9 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/ExtItemLookupService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/ExtItemLookupService.java @@ -9,6 +9,7 @@ import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemSearch; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.barcodeLookup.BarcodeLookupService; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.dataKick.DatakickService; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.rebrickable.RebrickableService; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.ItemKind; @@ -38,10 +39,12 @@ private static > List getProductProviderInfo() { diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupClient.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupClient.java index 23acf79091..4e458ce3a5 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupClient.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupClient.java @@ -1,20 +1,20 @@ package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.barcodeLookup; -import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import io.opentelemetry.instrumentation.annotations.WithSpan; +import io.quarkus.cache.CacheResult; import io.smallrye.mutiny.Uni; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.QueryParam; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; -import java.util.concurrent.CompletionStage; - @Path("/v3/products") @RegisterRestClient(configKey = "barcodelookup-com") public interface BarcodeLookupClient { @WithSpan @GET - Uni getFromUpcCode(@QueryParam("key") String apiKey, @QueryParam("barcode") String barcode); + @CacheResult(cacheName = "barcodelookup-barcode-search") + Uni searchBarcode(@QueryParam("key") String apiKey, @QueryParam("barcode") String barcode); } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupService.java index 012872b688..ac58042794 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupService.java @@ -1,151 +1,190 @@ -//package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.barcodeLookup; -// -//import com.fasterxml.jackson.databind.JsonNode; -//import com.fasterxml.jackson.databind.node.ArrayNode; -//import com.fasterxml.jackson.databind.node.ObjectNode; -//import io.opentelemetry.instrumentation.annotations.WithSpan; -//import jakarta.enterprise.context.ApplicationScoped; -//import jakarta.inject.Inject; -//import lombok.Getter; -//import lombok.NoArgsConstructor; -//import lombok.extern.slf4j.Slf4j; -//import org.eclipse.microprofile.config.inject.ConfigProperty; -//import org.eclipse.microprofile.rest.client.inject.RestClient; -//import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; -//import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResult; -//import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; -//import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.product.ApiProductSearchService; -// -//import java.net.URL; -//import java.util.*; -//import java.util.concurrent.CompletionStage; -// -///** -// * -// * - https://www.barcodelookup.com/api -// */ -//@ApplicationScoped -//@Slf4j -//@NoArgsConstructor -//public class BarcodeLookupService extends ItemSearchService { -// -// BarcodeLookupClient barcodeLookupClient; -// @Getter -// ExtItemLookupProviderInfo providerInfo; -// String apiKey; -// -// @Inject -// public BarcodeLookupService( -// @RestClient -// BarcodeLookupClient barcodeLookupClient, -// @ConfigProperty(name = "productLookup.providers.barcodelookup-com.displayName") -// String displayName, -// @ConfigProperty(name = "productLookup.providers.barcodelookup-com.enabled", defaultValue = "false") -// boolean enabled, -// @ConfigProperty(name = "productLookup.providers.barcodelookup-com.description", defaultValue = "") -// String description, -// @ConfigProperty(name = "productLookup.providers.barcodelookup-com.acceptsContributions", defaultValue = "") -// boolean acceptsContributions, -// @ConfigProperty(name = "productLookup.providers.barcodelookup-com.homepage", defaultValue = "") -// URL homepage, -// @ConfigProperty(name = "productLookup.providers.barcodelookup-com.cost", defaultValue = "") -// String cost, -// @ConfigProperty(name = "productLookup.providers.barcodelookup-com.apiKey", defaultValue = "") -// String apiKey -// ) { -// this.barcodeLookupClient = barcodeLookupClient; -// -// ExtItemLookupProviderInfo.Builder infoBuilder = ExtItemLookupProviderInfo -// .builder() -// .displayName(displayName) -// .description(description) -// .acceptsContributions(acceptsContributions) -// .homepage(homepage) -// .cost(cost); -// -// if (apiKey == null || apiKey.isBlank()) { -// log.warn("API key for {} was null or blank.", displayName); -// infoBuilder.enabled(false); -// this.apiKey = null; -// } else { -// infoBuilder.enabled(enabled); -// this.apiKey = apiKey; -// } -// -// this.providerInfo = infoBuilder.build(); -// } -// -// @Override -// public boolean isEnabled() { -// return this.getProviderInfo().isEnabled(); -// } -// -// /** -// * https://www.barcodelookup.com/api -// * -// * @param results -// * -// * @return -// */ -// @WithSpan -// @Override -// public List jsonNodeToSearchResults(JsonNode results) { -// log.debug("Data from BarcodeLookup: {}", results.toPrettyString()); -// -// ArrayNode resultsAsArr = (ArrayNode) results.get("products"); -// List resultList = new ArrayList<>(resultsAsArr.size()); -// -// for (JsonNode result : resultsAsArr) { -// ObjectNode curResultJson = (ObjectNode) result; -// String brandName = ""; -// String name = ""; -// Map attributes = new HashMap<>(); -// -// for (Iterator> iter = curResultJson.fields(); iter.hasNext(); ) { -// Map.Entry curField = iter.next(); -// -// if (curField.getValue().isObject() || curField.getValue().isArray()) { -// //TODO:: handle? -// continue; -// } -// -// String curFieldName = curField.getKey(); -// String curFieldVal = curField.getValue().asText(); -// -// if (curField.getValue().isNull() || curFieldVal == null || curFieldVal.isBlank()) { -// continue; -// } -// -// switch (curFieldName) { -// case "brand": -// brandName = curFieldVal; -// break; -// case "title": -// name = curFieldVal; -// break; -// default: -// attributes.put(curFieldName, curFieldVal); -// } -// } -// -// resultList.add( -// ExtItemLookupResult -// .builder() -// .source(this.getProviderInfo().getDisplayName()) -// .name(name) -// .brand(brandName) -// .unifiedName(name) -// .attributes(attributes) -// .build() -// ); -// } -// -// return resultList; -// } -// -// @WithSpan -// @Override -// protected CompletionStage performBarcodeSearchCall(String barcode) { -// return this.barcodeLookupClient.getFromUpcCode(this.apiKey, barcode); -// } -//} +package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.barcodeLookup; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.opentelemetry.instrumentation.annotations.WithSpan; +import io.smallrye.mutiny.Multi; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.jboss.resteasy.reactive.ClientWebApplicationException; +import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResult; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResultNoResults; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupType; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.ResultMappingUtils; + +import java.net.URI; +import java.util.*; +import java.util.concurrent.CompletionStage; + +/** + * + * - https://www.barcodelookup.com/api + */ +@ApplicationScoped +@Slf4j +@NoArgsConstructor +public class BarcodeLookupService extends ItemSearchService { + + BarcodeLookupClient barcodeLookupClient; + @Getter + ExtItemLookupProviderInfo providerInfo; + String apiKey; + + @Inject + public BarcodeLookupService( + @RestClient + BarcodeLookupClient barcodeLookupClient, + @ConfigProperty(name = "productLookup.providers.barcodelookup-com.enabled", defaultValue = "false") + boolean enabled, + @ConfigProperty(name = "productLookup.providers.barcodelookup-com.apiKey", defaultValue = "") + String apiKey + ) { + this.barcodeLookupClient = barcodeLookupClient; + + ExtItemLookupProviderInfo.Builder infoBuilder = ExtItemLookupProviderInfo + .builder() + .id("barcodelookup-com") + .displayName("BarcodeLookup.com") + .description("Comprehensive database of products, but a paid service. Can get a 2-week trial API key.") + .acceptsContributions(true) + .homepage(URI.create("https://www.barcodelookup.com/")) + .cost("Paid"); + + if (apiKey == null || apiKey.isBlank()) { + log.warn("API key for BarcodeLookup was null or blank."); + infoBuilder.enabled(false); + this.apiKey = null; + } else { + infoBuilder.enabled(enabled); + this.apiKey = apiKey; + } + + this.providerInfo = infoBuilder.build(); + } + + @Override + public boolean isEnabled() { + return this.getProviderInfo().isEnabled(); + } + + /** + * https://www.barcodelookup.com/api + * + * @param results + * + * @return + */ + public Collection jsonNodeToSearchResults(LookupType type, JsonNode results) { + log.debug("Data from BarcodeLookup: {}", results.toPrettyString()); + + ArrayNode resultsAsArr = (ArrayNode) results.get("products"); + List resultList = new ArrayList<>(resultsAsArr.size()); + + for (JsonNode result : resultsAsArr) { + ObjectNode curResultJson = (ObjectNode) result; + ExtItemLookupResult.Builder resultBuilder = this.setupResponseBuilder(ExtItemLookupResult.builder(), type); + + + Map attributes = new HashMap<>(); + Map identifiers = new HashMap<>(); + Map links = new HashMap<>(); + List images = new ArrayList<>(); + + for (Iterator> iter = curResultJson.fields(); iter.hasNext(); ) { + Map.Entry curField = iter.next(); + + String curFieldName = curField.getKey(); + JsonNode curFieldVal = curField.getValue(); + + if(ResultMappingUtils.isFieldEmpty(curFieldVal)){ + continue; + } + + switch (curFieldName) { + case "brand": + case "asin": + case "mpn": + case "model": + identifiers.put(curFieldName, curFieldVal.asText()); + break; + case "barcode_formats": + for(String curBarcode : curFieldVal.asText().split(", ")){ + String[] barcodeParts = curBarcode.split(" "); + identifiers.put(barcodeParts[0], barcodeParts[1]); + } + break; + case "title": + resultBuilder.name(curFieldVal.asText()); + resultBuilder.unifiedName(curFieldVal.asText()); + break; + case "description": + resultBuilder.description(curFieldVal.asText()); + break; + case "images": + images.addAll( + curFieldVal.valueStream().map(JsonNode::asText).toList() + ); + break; + case "stores": + curFieldVal.valueStream() + .forEach(curStoreJson -> { + links.put(curStoreJson.get("name").asText(), curStoreJson.get("link").asText()); + }); + break; + default: + if(curFieldVal.isTextual()){ + attributes.put(curFieldName, curFieldVal.asText()); + } + } + } + + resultList.add( + resultBuilder + .attributes(attributes) + .identifiers(identifiers) + .links(links) + .images(images) + .build() + ); + } + + return resultList; + } + + @Override + public Optional> searchBarcode(String search) { + return Optional.of( + this.barcodeLookupClient.searchBarcode(this.apiKey, search) + .map(results->this.jsonNodeToSearchResults(LookupType.BARCODE, results)) + .onFailure().recoverWithItem(e->this.handleErrorRetCollection(LookupType.BARCODE, e)) + + .onItem().transformToMulti(collection-> + Multi.createFrom().iterable(collection) + ) + ); + } + + @Override + protected Optional handleClientError(LookupType type, ClientWebApplicationException e) { + if (e.getResponse().getStatus() == 404) { + return Optional.of( + this.setupResponseBuilder(LookupResultNoResults.builder(), type) + .detail("No items found.") + .build() + ); + } + return Optional.empty(); + } +} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java index e8c3bdae57..b922d42fee 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java @@ -14,12 +14,15 @@ import lombok.extern.slf4j.Slf4j; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.jboss.resteasy.reactive.ClientWebApplicationException; import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResult; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResultNoResults; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupType; +import java.io.InputStream; import java.net.URI; import java.util.*; @@ -113,7 +116,6 @@ public Collection jsonNodeToSearchResults(LookupType type, ArrayNo .lookupType(type) .source(this.getProviderInfo().getDisplayName()) .name(name) - .brand(brandName) .unifiedName(name) .attributes(attributes) .build() @@ -134,4 +136,16 @@ public Optional> searchBarcode(String search) { ) ); } + + @Override + protected Optional handleClientError(LookupType type, ClientWebApplicationException e) { + if (e.getResponse().getStatus() == 404) { + return Optional.of( + this.setupResponseBuilder(LookupResultNoResults.builder(), type) + .detail("No items found.") + .build() + ); + } + return Optional.empty(); + } } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java index 709272ec42..58458dac81 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java @@ -23,6 +23,7 @@ import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.ItemKind; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupType; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.ResultMappingUtils; import java.io.InputStream; import java.net.URI; @@ -97,28 +98,20 @@ public LookupResult jsonNodeToSearchResults(LookupType type, ObjectNode results) log.info("Search result: {}", results); ExtItemLookupResult.Builder resultBuilder = ExtItemLookupResult.builder() .lookupType(type) - .source(this.getProviderInfo().getDisplayName()) - .brand(BRAND); + .source(this.getProviderInfo().getDisplayName()); List images = new ArrayList<>(); Map links = new HashMap<>(); Map identifiers = new HashMap<>(); Map attributes = new HashMap<>(); + attributes.put("brand", BRAND); + for (Map.Entry curField : results.properties()) { String curFieldName = curField.getKey(); JsonNode curFieldVal = curField.getValue(); - if (curField.getValue().isNull() || curFieldVal == null) { - continue; - } - if (curFieldVal.isTextual() && curFieldVal.asText().isBlank()) { - continue; - } - if (curFieldVal.isArray() && curFieldVal.isEmpty()) { - continue; - } - if (curFieldVal.isObject() && curFieldVal.isEmpty()) { + if(ResultMappingUtils.isFieldEmpty(curFieldVal)){ continue; } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupTypes.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupTypes.java new file mode 100644 index 0000000000..b4c3c0dfaa --- /dev/null +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupTypes.java @@ -0,0 +1,7 @@ +package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils; + +public final class LookupTypes { + public static final String BARCODE = "barcode"; + public static final String LEGO_PART_NUM = "legoPartNum"; + public static final String WEBPAGE = "webpage"; +} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/ResultMappingUtils.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/ResultMappingUtils.java new file mode 100644 index 0000000000..a1560bff1c --- /dev/null +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/ResultMappingUtils.java @@ -0,0 +1,22 @@ +package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils; + +import com.fasterxml.jackson.databind.JsonNode; + +public final class ResultMappingUtils { + + public static boolean isFieldEmpty(JsonNode curFieldVal) { + if (curFieldVal == null || curFieldVal.isNull()) { + return true; + } + if (curFieldVal.isTextual() && curFieldVal.asText().isBlank()) { + return true; + } + if (curFieldVal.isArray() && curFieldVal.isEmpty()) { + return true; + } + if (curFieldVal.isObject() && curFieldVal.isEmpty()) { + return true; + } + return false; + } +} diff --git a/software/plugins/external-item-search/src/main/resources/application.yml b/software/plugins/external-item-search/src/main/resources/application.yml index 76ff90acbc..95bec89921 100644 --- a/software/plugins/external-item-search/src/main/resources/application.yml +++ b/software/plugins/external-item-search/src/main/resources/application.yml @@ -6,13 +6,7 @@ service: productLookup: providers: barcodelookup-com: - displayName: BarcodeLookup.com - description: Comprehensive database of products, but a paid service. Can get a 2-week trial API key. - cost: Paid - acceptsContributions: true - homepage: https://www.barcodelookup.com/ enabled: true - url: https://api.barcodelookup.com/ apiKey: " " datakick: enabled: true From dcb7aaa7a81a2998aa896116706cf0793bfed6c6 Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Wed, 22 Apr 2026 01:43:19 -0400 Subject: [PATCH 11/38] Plugin - Ext Item Search - Adding back in upcitem database --- .../old/mappings/upcitemdb_trial_barcode.json | 127 ------------ .../dev/services/mappings/upcitemdb/README.md | 3 + .../upcitemdb}/upcitemdb_barcode.json | 0 .../upcitemdb/upcitemdb_trial_barcode.json | 181 ++++++++++++++++++ .../dev/services/wm_settings.json | 7 - .../service/ExtItemLookupService.java | 5 +- .../upcItemDb/UpcItemDbLookupClient.java | 13 +- .../upcItemDb/UpcItemDbService.java | 101 +++++----- .../src/main/resources/application.yml | 6 - 9 files changed, 247 insertions(+), 196 deletions(-) delete mode 100644 software/plugins/external-item-search/dev/old/mappings/upcitemdb_trial_barcode.json create mode 100644 software/plugins/external-item-search/dev/services/mappings/upcitemdb/README.md rename software/plugins/external-item-search/dev/{old/mappings => services/mappings/upcitemdb}/upcitemdb_barcode.json (100%) create mode 100644 software/plugins/external-item-search/dev/services/mappings/upcitemdb/upcitemdb_trial_barcode.json delete mode 100644 software/plugins/external-item-search/dev/services/wm_settings.json rename software/plugins/external-item-search/{temp/api/product => src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers}/upcItemDb/UpcItemDbLookupClient.java (70%) rename software/plugins/external-item-search/{temp/api/product => src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers}/upcItemDb/UpcItemDbService.java (57%) diff --git a/software/plugins/external-item-search/dev/old/mappings/upcitemdb_trial_barcode.json b/software/plugins/external-item-search/dev/old/mappings/upcitemdb_trial_barcode.json deleted file mode 100644 index def1116ed6..0000000000 --- a/software/plugins/external-item-search/dev/old/mappings/upcitemdb_trial_barcode.json +++ /dev/null @@ -1,127 +0,0 @@ -{ - "request": { - "urlPathPattern": "/upcitemdb/prod/trial/lookup", - "method": "GET" - }, - "response": { - "status": 200, - "headers": { - "Content-Type": "application/json" - }, - "jsonBody": { - "code" : "OK", - "total" : 1, - "offset" : 0, - "items" : [ { - "ean" : "9781492057895", - "title" : "Building Event-Driven Microservices - by Adam Bellemare (Paperback)", - "description" : "", - "isbn" : "9781492057895", - "publisher" : "O'Reilly Media", - "category" : "Media > Books", - "images" : [ "https://target.scene7.com/is/image/Target/GUEST_324e0d39-af27-40bf-84a5-edead2a6b8d6?wid=1000&hei=1000", "http://images.ecampus.com/images/d/7/895/9781492057895.jpg", "https://i5.walmartimages.com/asr/543ea27b-8e97-4935-b34e-129c4df7dc09.cd0c7517581efa4e9310fa3eb3b0544b.jpeg?odnHeight=450&odnWidth=450&odnBg=ffffff", "https://dynamic.indigoimages.ca//books/9781492057895.jpg?width=200&maxheight=200", "http://images.biggerbooks.com/images/d/7/895/9781492057895.jpg", "http://www0.alibris-static.com/isbn/9781492057895.gif", "https://covers.vitalsource.com/vbid/9781492057840/width/1024" ], - "offers" : [ { - "merchant" : "Indigo Books & Music", - "domain" : "chapters.indigo.ca", - "title" : "Building Event-driven Microservices: Leveraging Organizational Data At Scale", - "currency" : "CAD", - "list_price" : "", - "price" : 79.99, - "shipping" : "", - "condition" : "New", - "availability" : "", - "link" : "https://www.upcitemdb.com/norob/alink/?id=v2p2z2t24353a4a4w2&tid=3&seq=1717964474&plt=1d6fc504705ea0b2865a7d1f675c78c8", - "updated_t" : 1657896126 - }, { - "merchant" : "eCampus.com", - "domain" : "ecampus.com", - "title" : "Building Event-driven Microservices", - "currency" : "", - "list_price" : "", - "price" : 44.99, - "shipping" : "", - "condition" : "New", - "availability" : "", - "link" : "https://www.upcitemdb.com/norob/alink/?id=v2p2z2z203z2e494q2&tid=3&seq=1717964474&plt=7fe56d36fed49f8897fbd1d4735ba8b7", - "updated_t" : 1717829196 - }, { - "merchant" : "BiggerBooks.com", - "domain" : "biggerbooks.com", - "title" : "Building Event-driven Microservices", - "currency" : "", - "list_price" : "", - "price" : 46.34, - "shipping" : "", - "condition" : "New", - "availability" : "", - "link" : "https://www.upcitemdb.com/norob/alink/?id=v2p2z2z213x2b4a4q2&tid=3&seq=1717964474&plt=7766236d207034f7a5a9326410f98340", - "updated_t" : 1709792997 - }, { - "merchant" : "Wal-Mart.com", - "domain" : "walmart.com", - "title" : "Building Event-Driven Microservices : Leveraging Organizational Data at Scale (Paperback)", - "currency" : "", - "list_price" : "", - "price" : 47.63, - "shipping" : "", - "condition" : "New", - "availability" : "", - "link" : "https://www.upcitemdb.com/norob/alink/?id=v2p2030333036464v2&tid=3&seq=1717964474&plt=48230f0aec834457e8a35d86df4500dd", - "updated_t" : 1645036303 - }, { - "merchant" : "Alibris", - "domain" : "alibris.com", - "title" : "building event driven microservices leveraging organizational data at scale", - "currency" : "", - "list_price" : "", - "price" : 30.19, - "shipping" : "", - "condition" : "New", - "availability" : "", - "link" : "https://www.upcitemdb.com/norob/alink/?id=v2q2y203w263f4d4z2&tid=3&seq=1717964474&plt=459a4896c1c59342487c3a75a6340ab0", - "updated_t" : 1694566499 - }, { - "merchant" : "Alibris UK", - "domain" : "alibris.co.uk", - "title" : "building event driven microservices leveraging organizational data at scale", - "currency" : "GBP", - "list_price" : "", - "price" : 37.19, - "shipping" : "", - "condition" : "New", - "availability" : "", - "link" : "https://www.upcitemdb.com/norob/alink/?id=v2q2x2y213638454v2&tid=3&seq=1717964474&plt=61a7f99977bc01fc4d24bea6a9a4fd31", - "updated_t" : 1694564012 - }, { - "merchant" : "Target", - "domain" : "target.com", - "title" : "Building Event-Driven Microservices - by Adam Bellemare (Paperback)", - "currency" : "", - "list_price" : "", - "price" : 36.49, - "shipping" : "", - "condition" : "New", - "availability" : "", - "link" : "https://www.upcitemdb.com/norob/alink/?id=v2p213z2x233c4c4t2&tid=3&seq=1717964474&plt=a2c1e762c0e1e99f4a8e3f8659003e19", - "updated_t" : 1717738401 - }, { - "merchant" : "VitalSource ", - "domain" : "vitalsource.com", - "title" : "Building Event-Driven Microservices - 1st Edition (eBook)", - "currency" : "CAD", - "list_price" : "", - "price" : 71.99, - "shipping" : "", - "condition" : "New", - "availability" : "", - "link" : "https://www.upcitemdb.com/norob/alink/?id=v2q23303335394b4x2&tid=3&seq=1717964474&plt=b5f017d14cdf910f7f9ecf308ba71a8a", - "updated_t" : 1714893523 - } ] - } ] - } - } -} - - - - diff --git a/software/plugins/external-item-search/dev/services/mappings/upcitemdb/README.md b/software/plugins/external-item-search/dev/services/mappings/upcitemdb/README.md new file mode 100644 index 0000000000..f320e156f5 --- /dev/null +++ b/software/plugins/external-item-search/dev/services/mappings/upcitemdb/README.md @@ -0,0 +1,3 @@ +# UPC Item Database + + - \ No newline at end of file diff --git a/software/plugins/external-item-search/dev/old/mappings/upcitemdb_barcode.json b/software/plugins/external-item-search/dev/services/mappings/upcitemdb/upcitemdb_barcode.json similarity index 100% rename from software/plugins/external-item-search/dev/old/mappings/upcitemdb_barcode.json rename to software/plugins/external-item-search/dev/services/mappings/upcitemdb/upcitemdb_barcode.json diff --git a/software/plugins/external-item-search/dev/services/mappings/upcitemdb/upcitemdb_trial_barcode.json b/software/plugins/external-item-search/dev/services/mappings/upcitemdb/upcitemdb_trial_barcode.json new file mode 100644 index 0000000000..493d5ffc6a --- /dev/null +++ b/software/plugins/external-item-search/dev/services/mappings/upcitemdb/upcitemdb_trial_barcode.json @@ -0,0 +1,181 @@ +{ + "request": { + "urlPathPattern": "/upcitemdb/prod/trial/lookup", + "method": "GET", + "queryParameters": { + "upc": { + "equalTo": "017078987621" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "delayDistribution": { + "type": "lognormal", + "median": 5000, + "sigma": 0.4 + }, + "jsonBody": { + "code": "OK", + "total": 1, + "offset": 0, + "items": [ + { + "ean": "0017078987621", + "title": "IMAX / Solarmax (DVD + CD) Sling Shot Documentary", + "description": "Ever 11 years the sun s poles reverse with unimaginable violence. The peak of the storm is called a solarmax. The hottest film under the sun Solarmax is a breathtaking exploration into the awesome vastness and mysterious power of our closest star.", + "upc": "017078987621", + "brand": "Slingshot", + "model": "08443793", + "color": "Y", + "size": "", + "dimension": "7.8 X 5.8 X 0.8 inches", + "weight": "0.4 Pounds", + "category": "Media > DVDs & Videos", + "currency": "CAD", + "lowest_recorded_price": 0.01, + "highest_recorded_price": 32.62, + "images": [ + "https://i5.walmartimages.com/asr/8317b21f-bbce-45f8-b0dc-09c9cf144c2f.e384a8a897b49fa5aea6027ac82a861f.jpeg?odnHeight=450&odnWidth=450&odnBg=ffffff", + "http://img.bbystatic.com/BestBuy_US/images/products/1185/11858983_sc.jpg", + "https://target.scene7.com/is/image/Target/GUEST_9cd083de-37dd-4897-9e43-910e3cbe5397?wid=1000&hei=1000", + "http://dynamic.indigoimages.ca/dvd/017078987621.jpg?width=200&maxheight=200", + "https://tshop.r10s.com/486/53e/f00d/b55f/50ae/1c0e/52a8/1119e88196c4544488dc07.jpg?_ex=512x512", + "http://www.fye.com/amgcover/dvd/full/t1/88/t188041fs34.jpg", + "http://i.sdcd.us/b/500/6/4/9/9/419946.jpg", + "http://images.11main.com/s3/11main-images/product/8290004/image/4d44f8e3a19c2b08c8f1b6efb66780e8.jpeg?width=200&height=200", + "http://8016235491c6828f9cae-6b0d87410f7cc1525cc32b79408788c4.r96.cf2.rackcdn.com/1979/57905357_1.jpg" + ], + "offers": [ + { + "merchant": "Indigo Books & Music", + "domain": "chapters.indigo.ca", + "title": "IMAX - SolarMax", + "currency": "CAD", + "list_price": "", + "price": 7.99, + "shipping": "Free Shipping", + "condition": "New", + "availability": "Out of Stock", + "link": "https://www.upcitemdb.com/norob/alink/?id=u2w263034313e474&tid=1&seq=1776834168&plt=374c62124fd02b491f36e18003f6ea45", + "updated_t": 1425355586 + }, + { + "merchant": "Rakuten(Buy.com)", + "domain": "rakuten.com", + "title": "Imax-Solar Max Collectors Editions", + "currency": "", + "list_price": "", + "price": 26.1, + "shipping": "", + "condition": "New", + "availability": "", + "link": "https://www.upcitemdb.com/norob/alink/?id=u2o263t22303d454x2&tid=1&seq=1776834168&plt=8c48b19445357ec61c5d14446ba3a4b4", + "updated_t": 1554563058 + }, + { + "merchant": "Best Buy", + "domain": "bestbuy.com", + "title": "Solarmax (2 Disc) (collector's Edition) (dvd)", + "currency": "", + "list_price": "", + "price": 9.99, + "shipping": "", + "condition": "New", + "availability": "", + "link": "https://www.upcitemdb.com/norob/alink/?id=w2w243t223238494&tid=1&seq=1776834168&plt=85d1df534b50cb255dba45976d8dcf8d", + "updated_t": 1484176933 + }, + { + "merchant": "Fye.com", + "domain": "fye.com", + "title": "Solarmax [2 Discs] (new)", + "currency": "", + "list_price": "", + "price": 3, + "shipping": "", + "condition": "New", + "availability": "", + "link": "https://www.upcitemdb.com/norob/alink/?id=w2x2x2y233636494&tid=1&seq=1776834168&plt=f7d97afa8d7518782f89242cd99073e5", + "updated_t": 1514927093 + }, + { + "merchant": "Wal-Mart.com", + "domain": "walmart.com", + "title": "IMAX / Solarmax (DVD + CD) Sling Shot Documentary", + "currency": "", + "list_price": 13.44, + "price": 9.99, + "shipping": "Free Shipping", + "condition": "New", + "availability": "Out of Stock", + "link": "https://www.upcitemdb.com/norob/alink/?id=13t2y2x2x20394a4&tid=1&seq=1776834168&plt=59bd8fcaabdad1ac8055a91f6e2a67a6", + "updated_t": 1771790346 + }, + { + "merchant": "DVD Planet", + "domain": "dvdplanet.com", + "title": "Solar Max - DVD", + "currency": "", + "list_price": "", + "price": 10.22, + "shipping": "", + "condition": "New", + "availability": "", + "link": "https://www.upcitemdb.com/norob/alink/?id=13v203u2v2536464&tid=1&seq=1776834168&plt=6275bfa7a265be9b0f848967f484576d", + "updated_t": 1450411436 + }, + { + "merchant": "Target", + "domain": "target.com", + "title": "IMAX-SOLAR MAX COLLECTORS EDITIONS (DVD)", + "currency": "", + "list_price": "", + "price": 19.89, + "shipping": "", + "condition": "New", + "availability": "", + "link": "https://www.upcitemdb.com/norob/alink/?id=23q2x2x24313c4a4&tid=1&seq=1776834168&plt=27ac62da0fd38aaacf190c45c01d6086", + "updated_t": 1587271676 + }, + { + "merchant": "11 Main", + "domain": "11Main.com", + "title": "SOLARMAX CE (IMAX)", + "currency": "", + "list_price": "", + "price": 22.47, + "shipping": "", + "condition": "New", + "availability": "", + "link": "https://www.upcitemdb.com/norob/alink/?id=u2r2x2w23363c4b4y2&tid=1&seq=1776834168&plt=0d535055cd08b6aeed3487195f1a844f", + "updated_t": 1437986502 + }, + { + "merchant": "Pricefalls.com", + "domain": "pricefalls.com", + "title": "Solar Max DVD", + "currency": "", + "list_price": "", + "price": 12.33, + "shipping": "", + "condition": "New", + "availability": "", + "link": "https://www.upcitemdb.com/norob/alink/?id=v2r2x2v2y213a484t2&tid=1&seq=1776834168&plt=9e5ff70dc03c951731a8c6ef1bdc7c6c", + "updated_t": 1484934798 + } + ], + "asin": "B000063K1N", + "elid": "173714700508" + } + ] + } + } +} + + + + diff --git a/software/plugins/external-item-search/dev/services/wm_settings.json b/software/plugins/external-item-search/dev/services/wm_settings.json deleted file mode 100644 index 72645a5d58..0000000000 --- a/software/plugins/external-item-search/dev/services/wm_settings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "delayDistribution": { - "type": "lognormal", - "median": 5000, - "sigma": 0.4 - } -} \ No newline at end of file diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/ExtItemLookupService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/ExtItemLookupService.java index ddda9abfd9..df1647f34a 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/ExtItemLookupService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/ExtItemLookupService.java @@ -12,6 +12,7 @@ import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.barcodeLookup.BarcodeLookupService; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.dataKick.DatakickService; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.rebrickable.RebrickableService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.upcItemDb.UpcItemDbService; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.ItemKind; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupType; import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; @@ -40,11 +41,13 @@ private static > List getProductProviderInfo() { diff --git a/software/plugins/external-item-search/temp/api/product/upcItemDb/UpcItemDbLookupClient.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/upcItemDb/UpcItemDbLookupClient.java similarity index 70% rename from software/plugins/external-item-search/temp/api/product/upcItemDb/UpcItemDbLookupClient.java rename to software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/upcItemDb/UpcItemDbLookupClient.java index 7de15973d9..0c2437c43e 100644 --- a/software/plugins/external-item-search/temp/api/product/upcItemDb/UpcItemDbLookupClient.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/upcItemDb/UpcItemDbLookupClient.java @@ -1,7 +1,10 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.product.upcItemDb; +package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.upcItemDb; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import io.opentelemetry.instrumentation.annotations.WithSpan; +import io.quarkus.cache.CacheResult; +import io.smallrye.mutiny.Uni; import jakarta.validation.constraints.NotNull; import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; @@ -11,14 +14,15 @@ import java.util.concurrent.CompletionStage; @Path("/prod/") -@RegisterRestClient(configKey = "upc-upcitemdb") +@RegisterRestClient(configKey = "upcitemdb") public interface UpcItemDbLookupClient { @WithSpan @POST @Path("v1/lookup") @Consumes(MediaType.APPLICATION_JSON) - CompletionStage getFromUpcCode( + @CacheResult(cacheName = "upcitemdb-barcode-search") + Uni getFromUpcCode( @HeaderParam("user_key") String key, @HeaderParam("key_type") String keyType, Request barcode @@ -28,7 +32,8 @@ CompletionStage getFromUpcCode( @GET @Path("trial/lookup") @Consumes(MediaType.APPLICATION_JSON) - CompletionStage getFromUpcCodeTrial( + @CacheResult(cacheName = "upcitemdb-trial-barcode-search") + Uni getFromUpcCodeTrial( @QueryParam("upc") String barcode ); diff --git a/software/plugins/external-item-search/temp/api/product/upcItemDb/UpcItemDbService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/upcItemDb/UpcItemDbService.java similarity index 57% rename from software/plugins/external-item-search/temp/api/product/upcItemDb/UpcItemDbService.java rename to software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/upcItemDb/UpcItemDbService.java index 8eb97a9ba3..49fc52dd05 100644 --- a/software/plugins/external-item-search/temp/api/product/upcItemDb/UpcItemDbService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/upcItemDb/UpcItemDbService.java @@ -1,9 +1,11 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.product.upcItemDb; +package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.upcItemDb; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import io.opentelemetry.instrumentation.annotations.WithSpan; +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import lombok.Getter; @@ -13,11 +15,13 @@ import org.eclipse.microprofile.rest.client.inject.RestClient; import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResult; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.product.ApiProductSearchService; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupType; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.ResultMappingUtils; -import java.net.URL; +import java.net.URI; import java.util.*; -import java.util.concurrent.CompletionStage; /** * @@ -27,7 +31,7 @@ @ApplicationScoped @Slf4j @NoArgsConstructor -public class UpcItemDbService extends ApiProductSearchService { +public class UpcItemDbService extends ItemSearchService { @Inject @RestClient @@ -41,18 +45,8 @@ public class UpcItemDbService extends ApiProductSearchService { public UpcItemDbService( @RestClient UpcItemDbLookupClient upcItemDbLookupClient, - @ConfigProperty(name = "productLookup.providers.upcitemdb.displayName") - String displayName, @ConfigProperty(name = "productLookup.providers.upcitemdb.enabled", defaultValue = "false") boolean enabled, - @ConfigProperty(name = "productLookup.providers.upcitemdb.description", defaultValue = "") - String description, - @ConfigProperty(name = "productLookup.providers.upcitemdb.acceptsContributions", defaultValue = "") - boolean acceptsContributions, - @ConfigProperty(name = "productLookup.providers.upcitemdb.homepage", defaultValue = "") - URL homepage, - @ConfigProperty(name = "productLookup.providers.upcitemdb.cost", defaultValue = "") - String cost, @ConfigProperty(name = "productLookup.providers.upcitemdb.apiKey", defaultValue = "") String apiKey ) { @@ -60,12 +54,13 @@ public UpcItemDbService( this.apiKey = apiKey; this.providerInfo = ExtItemLookupProviderInfo .builder() - .displayName(displayName) + .id("upcitemdb-com") + .displayName("upcitemdb.com") + .description("A lookup database with good number of records, and a free tier with 100 requests per day.") + .acceptsContributions(false) + .homepage(URI.create("https://www.upcitemdb.com/")) + .cost("Paid, Free tier") .enabled(enabled) - .description(description) - .acceptsContributions(acceptsContributions) - .homepage(homepage) - .cost(cost) .build(); } @@ -78,59 +73,53 @@ public boolean hasKey() { return this.apiKey != null && !this.apiKey.isBlank(); } - private ExtItemLookupResult jsonToResult(ObjectNode json) { - String brandName = ""; - String name = ""; - Map attributes = new HashMap<>(); - ExtItemLookupResult.Builder resultBuilder = ExtItemLookupResult.builder(); + private ExtItemLookupResult jsonToResult(LookupType type, ObjectNode json) { + ExtItemLookupResult.Builder resultBuilder = this.setupResponseBuilder(ExtItemLookupResult.builder(), type); + Map attributes = new HashMap<>(); + Map identifiers = new HashMap<>(); + List images = new ArrayList<>(); - for (Iterator> iter = json.fields(); iter.hasNext(); ) { - Map.Entry curField = iter.next(); + for (Map.Entry curField : json.properties()) { String curFieldName = curField.getKey(); JsonNode curFieldVal = curField.getValue(); - - if (curField.getValue().isNull() || curFieldVal == null) { + if (ResultMappingUtils.isFieldEmpty(curFieldVal)) { continue; } switch (curFieldName) { case "ean": - resultBuilder.barcode(curFieldVal.asText()); - break; - case "brand": - brandName = curFieldVal.asText(); + case "upc": + case "asin": + case "elid": + identifiers.put(curFieldName, curFieldVal.asText()); break; case "title": - name = curFieldVal.asText(); + resultBuilder.name(curFieldVal.asText()); + resultBuilder.unifiedName(curFieldVal.asText()); break; case "description": resultBuilder.description(curFieldVal.asText()); break; case "images": - ArrayList images = new ArrayList<>(curFieldVal.size()); - for (JsonNode curImg : (ArrayNode) curFieldVal) { + for (JsonNode curImg : curFieldVal) { images.add(curImg.asText()); } - resultBuilder.images(images); break; - default: { - String text = curFieldVal.asText(); - if (!text.isBlank()) { - attributes.put(curFieldName, text); + default: + if(curFieldVal.isTextual()){ + attributes.put(curFieldName, curFieldVal.asText()); } break; - } } } return resultBuilder .source(this.getProviderInfo().getDisplayName()) - .name(name) - .brand(brandName) - .unifiedName((brandName != null && !brandName.isBlank() ? brandName + " " + name : name)) .attributes(attributes) + .identifiers(identifiers) + .images(images) .build(); } @@ -142,23 +131,33 @@ private ExtItemLookupResult jsonToResult(ObjectNode json) { * @return */ @WithSpan - @Override - public List jsonNodeToSearchResults(JsonNode results) { + public Collection jsonNodeToSearchResults(LookupType type, ObjectNode results) { log.debug("Data from upcitemdb: {}", results.toPrettyString()); ArrayNode resultsAsArr = (ArrayNode) results.get("items"); - List resultList = new ArrayList<>(resultsAsArr.size()); + List resultList = new ArrayList<>(resultsAsArr.size()); for (JsonNode result : resultsAsArr) { - resultList.add(this.jsonToResult((ObjectNode) result)); + resultList.add(this.jsonToResult(type, (ObjectNode) result)); } return resultList; } - @WithSpan @Override - protected CompletionStage performBarcodeSearchCall(String barcode) { + public Optional> searchBarcode(String barcode) { + return Optional.of( + this.performBarcodeSearchCall(barcode) + .map(result->this.jsonNodeToSearchResults(LookupType.BARCODE, result)) + .onFailure().recoverWithItem(e->this.handleErrorRetCollection(LookupType.BARCODE, e)) + .onItem().transformToMulti(collection-> + Multi.createFrom().iterable(collection) + ) + ); + } + + + protected Uni performBarcodeSearchCall(String barcode) { if (this.hasKey()) { return this.upcItemDbLookupClient.getFromUpcCode( this.apiKey, diff --git a/software/plugins/external-item-search/src/main/resources/application.yml b/software/plugins/external-item-search/src/main/resources/application.yml index 95bec89921..3ebb0e0834 100644 --- a/software/plugins/external-item-search/src/main/resources/application.yml +++ b/software/plugins/external-item-search/src/main/resources/application.yml @@ -11,13 +11,7 @@ productLookup: datakick: enabled: true upcitemdb: - displayName: upcitemdb.com - description: A lookup database with good number of records, and a free tier with 100 requests per day. - cost: Free tier, Paid - acceptsContributions: false - homepage: https://www.upcitemdb.com/ enabled: true - url: https://api.upcitemdb.com apiKey: " " rebrickable: enabled: true From 6909f4811bca1c5dc15f4442fd2accc049de5117 Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Thu, 23 Apr 2026 21:44:24 -0400 Subject: [PATCH 12/38] Plugin - Ext Item Search - Completed yet another rework of how this nonsense is organized --- .../plugins/external-item-search/build.gradle | 66 +- .../services/mappings/barcodelookup/README.md | 12 +- .../barcodelookup_barcode_bad_key.json | 4 +- .../barcodelookup_barcode_no_results.json | 4 +- .../barcodelookup/barcodelookup_query.json | 525 ++++ .../barcodelookup_query_bad_key.json | 31 + .../barcodelookup_query_not_found.json | 32 + .../datakick/datakick_barcode_not_found.json | 32 +- ...part.json => rebrickable_part_by_num.json} | 0 ...n => rebrickable_part_by_num_bad_key.json} | 4 +- ...=> rebrickable_part_by_num_not_found.json} | 0 .../rebrickable/rebrickable_parts_search.json | 2232 +++++++++++++++++ .../rebrickable_parts_search_bad_key.json | 36 + .../rebrickable_parts_search_not_found.json | 39 + .../rebrickable/rebrickable_set_by_num.json | 36 + .../rebrickable_set_by_num_bad_key.json | 31 + .../rebrickable_set_by_num_not_found.json | 29 + .../rebrickable/rebrickable_sets_search.json | 68 + .../rebrickable_sets_search_bad_key.json | 36 + .../rebrickable_sets_search_not_found.json | 39 + .../mappings/upcitemdb/upcitemdb_barcode.json | 4 - .../interfaces/ItemLookupRestInterface.java | 3 +- .../model/ExtItemLookupProviderInfo.java | 22 +- .../extItemSearch/model/ExtItemSearch.java | 36 +- .../model/lookupResult/LookupResult.java | 13 +- .../service/ExtItemLookupService.java | 66 +- .../searchServices/ItemSearchService.java | 119 +- .../barcodeLookup/BarcodeLookupClient.java | 5 + .../barcodeLookup/BarcodeLookupService.java | 75 +- .../providers/dataKick/DatakickService.java | 78 +- .../rebrickable/RebrickableLookupClient.java | 25 +- .../rebrickable/RebrickableService.java | 171 +- .../providers/upcItemDb/UpcItemDbService.java | 90 +- .../searchServices/utils/ItemKind.java | 5 - .../{LookupType.java => LookupMethod.java} | 9 +- .../searchServices/utils/LookupService.java | 48 + .../searchServices/utils/LookupSource.java | 16 + .../searchServices/utils/LookupTypes.java | 7 - 38 files changed, 3688 insertions(+), 360 deletions(-) create mode 100644 software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_query.json create mode 100644 software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_query_bad_key.json create mode 100644 software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_query_not_found.json rename software/plugins/external-item-search/dev/services/mappings/rebrickable/{rebrickable_part.json => rebrickable_part_by_num.json} (100%) rename software/plugins/external-item-search/dev/services/mappings/rebrickable/{rebrickable_part_bad_key.json => rebrickable_part_by_num_bad_key.json} (88%) rename software/plugins/external-item-search/dev/services/mappings/rebrickable/{rebrickable_part_not_found.json => rebrickable_part_by_num_not_found.json} (100%) create mode 100644 software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_parts_search.json create mode 100644 software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_parts_search_bad_key.json create mode 100644 software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_parts_search_not_found.json create mode 100644 software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_set_by_num.json create mode 100644 software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_set_by_num_bad_key.json create mode 100644 software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_set_by_num_not_found.json create mode 100644 software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_sets_search.json create mode 100644 software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_sets_search_bad_key.json create mode 100644 software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_sets_search_not_found.json delete mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/ItemKind.java rename software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/{LookupType.java => LookupMethod.java} (63%) create mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupService.java create mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupSource.java delete mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupTypes.java diff --git a/software/plugins/external-item-search/build.gradle b/software/plugins/external-item-search/build.gradle index 68adfc861b..70e0f0b349 100644 --- a/software/plugins/external-item-search/build.gradle +++ b/software/plugins/external-item-search/build.gradle @@ -1,58 +1,58 @@ plugins { - id 'java' - id 'io.quarkus' - id "io.freefair.lombok" version "9.2.0" + id 'java' + id 'io.quarkus' + id "io.freefair.lombok" version "9.2.0" } group 'tech.ebp.openQuarterMaster' version '1.0.6-SNAPSHOT' repositories { - mavenCentral() - mavenLocal() + mavenCentral() + mavenLocal() } dependencies { - implementation enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}") - implementation 'io.quarkus:quarkus-rest' - implementation 'io.quarkus:quarkus-rest-client-jackson' - implementation 'io.quarkus:quarkus-rest-client' - implementation 'io.quarkus:quarkus-smallrye-openapi' - implementation 'io.quarkus:quarkus-rest-jackson' - implementation 'io.quarkus:quarkus-opentelemetry' - implementation 'io.quarkus:quarkus-config-yaml' - implementation 'io.quarkus:quarkus-smallrye-jwt' - implementation 'io.quarkus:quarkus-smallrye-health' - implementation 'io.quarkus:quarkus-micrometer' - implementation 'io.quarkus:quarkus-hibernate-validator' - implementation 'io.quarkus:quarkus-container-image-docker' - implementation 'io.quarkus:quarkus-cache' - implementation 'io.quarkus:quarkus-arc' + implementation enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}") + implementation 'io.quarkus:quarkus-rest' + implementation 'io.quarkus:quarkus-rest-client-jackson' + implementation 'io.quarkus:quarkus-rest-client' + implementation 'io.quarkus:quarkus-smallrye-openapi' + implementation 'io.quarkus:quarkus-rest-jackson' + implementation 'io.quarkus:quarkus-opentelemetry' + implementation 'io.quarkus:quarkus-config-yaml' + implementation 'io.quarkus:quarkus-smallrye-jwt' + implementation 'io.quarkus:quarkus-smallrye-health' + implementation 'io.quarkus:quarkus-micrometer' + implementation 'io.quarkus:quarkus-hibernate-validator' + implementation 'io.quarkus:quarkus-container-image-docker' + implementation 'io.quarkus:quarkus-cache' + implementation 'io.quarkus:quarkus-arc' - implementation group: 'org.jsoup', name: 'jsoup', version: '1.22.1' - implementation 'io.quarkiverse.wiremock:quarkus-wiremock:1.6.1' + implementation group: 'org.jsoup', name: 'jsoup', version: '1.22.1' + implementation 'io.quarkiverse.wiremock:quarkus-wiremock:1.6.1' - testImplementation 'io.quarkus:quarkus-junit5' - testImplementation 'io.rest-assured:rest-assured' + testImplementation 'io.quarkus:quarkus-junit5' + testImplementation 'io.rest-assured:rest-assured' // compileOnly 'io.quarkiverse.mockserver:quarkus-mockserver-test:1.9.0' } java { - sourceCompatibility = JavaVersion.VERSION_21 - targetCompatibility = JavaVersion.VERSION_21 + sourceCompatibility = JavaVersion.VERSION_25 + targetCompatibility = JavaVersion.VERSION_25 } test { - systemProperty "java.util.logging.manager", "org.jboss.logmanager.LogManager" + systemProperty "java.util.logging.manager", "org.jboss.logmanager.LogManager" } compileJava { - options.encoding = 'UTF-8' - options.compilerArgs << '-parameters' + options.encoding = 'UTF-8' + options.compilerArgs << '-parameters' } compileTestJava { - options.encoding = 'UTF-8' + options.encoding = 'UTF-8' } /** @@ -61,7 +61,7 @@ compileTestJava { * Used by the `makeInstallers.sh` script to get the version. */ tasks.register('printVersion') { - doLast { - println project.version - } + doLast { + println project.version + } } diff --git a/software/plugins/external-item-search/dev/services/mappings/barcodelookup/README.md b/software/plugins/external-item-search/dev/services/mappings/barcodelookup/README.md index 501b5d8c21..9f742d0c6c 100644 --- a/software/plugins/external-item-search/dev/services/mappings/barcodelookup/README.md +++ b/software/plugins/external-item-search/dev/services/mappings/barcodelookup/README.md @@ -1,4 +1,14 @@ # BarcodeLookup - https://www.barcodelookup.com/api - - https://www.barcodelookup.com/api-documentation \ No newline at end of file + - https://www.barcodelookup.com/api-documentation + +## Examples + +Barcode: + +`curl -H "Accept: application/json" "https://api.barcodelookup.com/v3/products?barcode=9780140157376&formatted=y&key={key}" | less` + +Query search: + +`curl -H "Accept: application/json" "https://api.barcodelookup.com/v3/products?search=GPS&formatted=y&key={key}"` diff --git a/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_barcode_bad_key.json b/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_barcode_bad_key.json index 271b8b7642..1b956d87ed 100644 --- a/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_barcode_bad_key.json +++ b/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_barcode_bad_key.json @@ -4,7 +4,9 @@ "method": "GET", "queryParameters": { "key": { - "equalTo": "devKeyBad" + "not": { + "equalTo": "devKey" + } }, "barcode": { "matches": ".*" diff --git a/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_barcode_no_results.json b/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_barcode_no_results.json index 0380a3a8a6..8402e0a289 100644 --- a/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_barcode_no_results.json +++ b/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_barcode_no_results.json @@ -7,7 +7,9 @@ "equalTo": "devKey" }, "barcode": { - "matches": "^((?!886736874135).)*$" + "not": { + "equalTo": "886736874135" + } } } }, diff --git a/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_query.json b/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_query.json new file mode 100644 index 0000000000..d8562f0db7 --- /dev/null +++ b/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_query.json @@ -0,0 +1,525 @@ +{ + "request": { + "urlPathPattern": "/barcodelookup/v3/products", + "method": "GET", + "queryParameters": { + "key": { + "equalTo": "devKey" + }, + "search": { + "equalTo": "GPS" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "delayDistribution": { + "type": "lognormal", + "median": 3000, + "sigma": 0.4 + }, + "jsonBody": { + "products": [ + { + "barcode_number": "4710887986774", + "barcode_formats": "EAN-13 4710887986774", + "mpn": "", + "model": "", + "asin": "B00LKQ7YFC", + "title": "2014 NEW Mivue 538 Dash Camera W/ Integrated GPS Receiver , Full HD, F1.8 Lens ,130?view Angle, Event Recording Mode , G Sensor", + "category": "Cameras & Optics > Cameras > Surveillance Cameras", + "manufacturer": "Mio", + "brand": "", + "contributors": [], + "age_group": "", + "ingredients": "", + "nutrition_facts": "", + "energy_efficiency_class": "", + "color": "", + "gender": "", + "material": "", + "pattern": "", + "format": "", + "multipack": "", + "size": "", + "length": "", + "width": "", + "height": "", + "weight": "", + "release_date": "", + "description": "", + "features": [], + "images": [ + "https://images.barcodelookup.com/5020/50202499-1.jpg", + "https://images.barcodelookup.com/5020/50202499-2.jpg", + "https://images.barcodelookup.com/5020/50202499-3.jpg", + "https://images.barcodelookup.com/5020/50202499-4.jpg", + "https://images.barcodelookup.com/5020/50202499-5.jpg" + ], + "last_update": "2021-06-22 02:27:01", + "stores": [], + "reviews": [] + }, + { + "barcode_number": "845251087574", + "barcode_formats": "UPC-A 845251087574, EAN-13 0845251087574", + "mpn": "", + "model": "", + "asin": "B00RKNTYAW", + "title": "Garmin Approach S1 GPS Golf Watch 1 Year Garmin Warranty (Certified Refurbished)", + "category": "Apparel & Accessories > Jewelry > Watches", + "manufacturer": "Garmin", + "brand": "", + "contributors": [], + "age_group": "", + "ingredients": "", + "nutrition_facts": "", + "energy_efficiency_class": "", + "color": "", + "gender": "", + "material": "", + "pattern": "", + "format": "", + "multipack": "", + "size": "", + "length": "", + "width": "", + "height": "", + "weight": "", + "release_date": "", + "description": "", + "features": [], + "images": [ + "https://images.barcodelookup.com/5020/50202500-1.jpg" + ], + "last_update": "2021-06-22 02:27:01", + "stores": [], + "reviews": [] + }, + { + "barcode_number": "817217847492", + "barcode_formats": "UPC-A 817217847492, EAN-13 0817217847492", + "mpn": "F02784", + "model": "", + "asin": "", + "title": "IRC GP-110 DOT/Dual Sport Tire 5.10-17 67S Rear Bias Tube Type", + "category": "Vehicles & Parts > Vehicle Parts & Accessories > Vehicle Maintenance, Care & Decor > Vehicle Covers > Vehicle Storage Covers > Automotive Storage Covers", + "manufacturer": "Irc", + "brand": "Irc", + "contributors": [], + "age_group": "", + "ingredients": "", + "nutrition_facts": "", + "energy_efficiency_class": "", + "color": "", + "gender": "", + "material": "", + "pattern": "", + "format": "", + "multipack": "", + "size": "", + "length": "", + "width": "", + "height": "", + "weight": "", + "release_date": "", + "description": "IRC GP-110 DOT/Dual Sport Tire 5.10-17 67S Rear Bias Tube Type condition: New Brand: IRC Manufacturer Part Number: F02784.", + "features": [], + "images": [ + "https://images.barcodelookup.com/16018/160184321-1.jpg" + ], + "last_update": "2021-06-22 02:28:30", + "stores": [ + { + "name": "Rakuten.com", + "country": "US", + "currency": "USD", + "currency_symbol": "$", + "price": "157.80", + "sale_price": "", + "tax": [], + "link": "https://www.rakuten.com/shop/bitstoreusa/product/46f-897778000312797/?sku=46f-897778000312797&scid=af_feed", + "item_group_id": "", + "availability": "", + "condition": "", + "shipping": [], + "last_update": "2021-06-22 04:57:25" + } + ], + "reviews": [] + }, + { + "barcode_number": "311529234396", + "barcode_formats": "UPC-A 311529234396, EAN-13 0311529234396", + "mpn": "", + "model": "", + "asin": "", + "title": "7 Inch 4GB 128MB FM Touch Screen Car TRUCK Navigation GPS SAT NAV Free Maps", + "category": "Electronics > Electronics Accessories", + "manufacturer": "", + "brand": "", + "contributors": [], + "age_group": "", + "ingredients": "", + "nutrition_facts": "", + "energy_efficiency_class": "", + "color": "", + "gender": "", + "material": "", + "pattern": "", + "format": "", + "multipack": "", + "size": "", + "length": "", + "width": "", + "height": "", + "weight": "", + "release_date": "", + "description": "Specification Brand : XGODY Model : 718 CPU : Media Tek MT3351 GPS Module : High Sensitive GPS Receiver Memory Size : 128MB RAM.", + "features": [], + "images": [ + "https://images.barcodelookup.com/27019/270191485-1.jpg" + ], + "last_update": "2022-06-14 23:12:05", + "stores": [ + { + "name": "Walmart", + "country": "US", + "currency": "USD", + "currency_symbol": "$", + "price": "71.99", + "sale_price": "", + "tax": [], + "link": "https://www.walmart.com/ip/7-Inch-4GB-128MB-FM-Touch-Screen-Car-TRUCK-Navigation-GPS-SAT-NAV-Free-Maps/531847854", + "item_group_id": "", + "availability": "", + "condition": "", + "shipping": [], + "last_update": "2022-06-14 23:12:05" + } + ], + "reviews": [] + }, + { + "barcode_number": "997964075963", + "barcode_formats": "UPC-A 997964075963, EAN-13 0997964075963", + "mpn": "3567219-10-3XL", + "model": "", + "asin": "", + "title": "Alpinestars GP X V2 Leather Riding Gloves (Black) 3XL (3X-Large)", + "category": "Apparel & Accessories > Shoes", + "manufacturer": "Alpinestars", + "brand": "Alpinestars", + "contributors": [], + "age_group": "", + "ingredients": "", + "nutrition_facts": "", + "energy_efficiency_class": "", + "color": "", + "gender": "", + "material": "", + "pattern": "", + "format": "", + "multipack": "", + "size": "", + "length": "", + "width": "", + "height": "", + "weight": "", + "release_date": "", + "description": "Alpinestars GP X v2 Leather Riding Gloves (Black) 3XL (3X-Large) condition: New with tags Brand: Alpinestars Manufacturer Part Number: 3567219-10-3XL Color: Black Gender: Men Size: 3XL (3X-Large).", + "features": [], + "images": [ + "https://images.barcodelookup.com/16018/160188689-1.jpg" + ], + "last_update": "2021-06-22 02:28:34", + "stores": [ + { + "name": "Rakuten.com", + "country": "US", + "currency": "USD", + "currency_symbol": "$", + "price": "185.78", + "sale_price": "", + "tax": [], + "link": "https://www.rakuten.com/shop/bitstoreusa/product/568-122299000283542/?sku=568-122299000283542&scid=af_feed", + "item_group_id": "", + "availability": "", + "condition": "", + "shipping": [], + "last_update": "2021-06-22 04:57:26" + } + ], + "reviews": [] + }, + { + "barcode_number": "997963380419", + "barcode_formats": "UPC-A 997963380419, EAN-13 0997963380419", + "mpn": "3556517-123-L", + "model": "", + "asin": "", + "title": "ALPINESTARS 2017 GP PLUS R Leather Racing/Riding Gloves (Black/White/Red) Large", + "category": "Apparel & Accessories > Shoes", + "manufacturer": "Alpinestars", + "brand": "Alpinestars", + "contributors": [], + "age_group": "", + "ingredients": "", + "nutrition_facts": "", + "energy_efficiency_class": "", + "color": "", + "gender": "", + "material": "", + "pattern": "", + "format": "", + "multipack": "", + "size": "", + "length": "", + "width": "", + "height": "", + "weight": "", + "release_date": "", + "description": "ALPINESTARS 2017 GP PLUS R Leather Racing/Riding Gloves (Black/White/Red) Large condition: New with tags Brand: Alpinestars Manufacturer Part Number: 3556517-123-L Color: Black/White/Red Gender: Men Size: L (Large).", + "features": [], + "images": [ + "https://images.barcodelookup.com/16018/160188718-1.jpg" + ], + "last_update": "2021-06-22 02:28:34", + "stores": [ + { + "name": "Rakuten.com", + "country": "US", + "currency": "USD", + "currency_symbol": "$", + "price": "286.67", + "sale_price": "", + "tax": [], + "link": "https://www.rakuten.com/shop/bitstoreusa/product/8ec-113083000293146/?sku=8ec-113083000293146&scid=af_feed", + "item_group_id": "", + "availability": "", + "condition": "", + "shipping": [], + "last_update": "2021-06-22 04:57:26" + } + ], + "reviews": [] + }, + { + "barcode_number": "858023644691", + "barcode_formats": "UPC-A 858023644691, EAN-13 0858023644691", + "mpn": "", + "model": "", + "asin": "", + "title": "Synergy Digital GPS Battery Compatible with Garmin DriveSmart 65 GPS (Li-ion 3.7V 750mAh) Ultra High Capacity Replacement for Garmin 361-00056-08", + "category": "Electronics > Audio > Stage Equipment > Wireless Transmitters", + "manufacturer": "", + "brand": "", + "contributors": [], + "age_group": "", + "ingredients": "", + "nutrition_facts": "", + "energy_efficiency_class": "", + "color": "", + "gender": "", + "material": "", + "pattern": "", + "format": "", + "multipack": "", + "size": "", + "length": "", + "width": "", + "height": "", + "weight": "", + "release_date": "", + "description": "Synergy Digital GPS Battery Compatible with Garmin DriveSmart 65 GPS (Li-ion 3.7V 750mAh) Ultra High Capacity Replacement for Garmin 361-00056-08 Battery.", + "features": [], + "images": [ + "https://images.barcodelookup.com/27019/270195388-1.jpg" + ], + "last_update": "2022-11-11 17:17:47", + "stores": [ + { + "name": "Walmart", + "country": "US", + "currency": "USD", + "currency_symbol": "$", + "price": "13.95", + "sale_price": "13.95", + "tax": [], + "link": "https://www.walmart.com/ip/Synergy-Digital-GPS-Battery-Compatible-Garmin-DriveSmart-65-GPS-Li-ion-3-7V-750mAh-Ultra-High-Capacity-Replacement-361-00056-08-Battery/538811806", + "item_group_id": "", + "availability": "", + "condition": "", + "shipping": [], + "last_update": "2022-11-11 17:17:47" + } + ], + "reviews": [] + }, + { + "barcode_number": "787860278526", + "barcode_formats": "UPC-A 787860278526, EAN-13 0787860278526", + "mpn": "", + "model": "", + "asin": "", + "title": "GoolRC RC Drone with Camera 4K 5G Wifi Brushless Motor GPS Optical Flow Positioning Track Flight RC Quadcopter for Kids and Adults", + "category": "Electronics", + "manufacturer": "", + "brand": "", + "contributors": [], + "age_group": "", + "ingredients": "", + "nutrition_facts": "", + "energy_efficiency_class": "", + "color": "", + "gender": "", + "material": "", + "pattern": "", + "format": "", + "multipack": "", + "size": "", + "length": "", + "width": "", + "height": "", + "weight": "", + "release_date": "", + "description": "Please note, this item ships from an international seller. Expected delivery is 10-15 days.Bugs 7 is amazing for experienced pilots with its advanced 1306 Brushless Motors, 4K WiFi Camera, 2.4 GHz Two-Way Communication technology, it means the flight will be more powerful and reliable, the image quality will be more excellent, and the control range is up to 300m. Features: Function: Up/down/forward/backward, turn left/right, sideward flight, LED light, 4K camera, brushless motor, track flight, follow me, point of interest flight, GPS positioning, optical flow positioning, headless mode, one key to take off/land/return. 4K Camera: Support 90-degree adjustable steering gear and 4K CMOS sensor to retain the most authentic details and make your creation much more inspirational. Dual Positioning: GPS system and optical positioning make the Bugs 7 completely aware of its location and relation to you. It hovers precisely, moves accurately, and locks onto satellites fast. 5G Wifi Transmission: The 5G Wifi real-time image transmission offers an amazing image and stabilized video for you to make fun of a new perspective from the air. Powerful Brushless Motor: 1306 Brushless motor features metal shell, good cooling performance, advanced inner electromagnetic construction, and working steadily at a high speed of 32km/h. Long Battery Life: 7.6V 1500mAH high-capacity battery with an energy-optimized system gives you a vastly improved flight experience. An up to 15 minutes' flight allows more creation. Track Flight: In this mode, you can just focus on composing, B7 will fly to the target or fly in the direction you tapped on the screen, and change the flight paths smoothly if you re-planed the track on the screen. After-sales Service Guarantee: Any problem, please contact our store without hesitation. Honor to serve you!n again. Specification: AIRCRAFT Size: 24 * 23.5 * 5.5cm/9.45 * 9.25 * 2.17in (unfolding); 14 * 7.5 * 5.5cm/5.51 * 2.95 * 2.17in(folding) Color: Black Max Service Flight Distance 300m Max. Flight Time 15 mins Max Ascent Speed 3 meter/s Max Descent Speed 2m/s Flight Height Limitation 120m Motor: Brushless 1306 Max Flight Speed: 32km/h CAMERA 1/2.7inch COMS Photo/Video Resolution 3840 * 2160p Image Transmission Resolution: 1280 * 720p Image Transmission System: 5G wifi Max. Image Transmission Distance 300m Mobile Device System iOS 9.0 or later /Android 4.4 or later Lens Angle 120 Adjustable Camera Angle 90 Tilt Rotation Range 0 to -90 Memory Card Supports Class 10 and above (maximum capacity 32GB) Local Video Frame Rate(In TF Card): 4K@16FPS, 2.5K@25FPS Maximum Frame Rate 720P@20FPS SD Card Support for 32GB Capacity Expansion Maximally, Class 10 or Up DRONE BATTERY Capacity: 1500 mAh Voltage: 7.6V/2A Rated Power: 15.2W Battery Type: LiPo Charging Time 150min REMOTE CONTROLLER Transmission Operating Frequency 2.4GHz Battery 2 * 1.5V AA Batteries (not included) Throttle mode(switchable): Mode 1(the throttle is the right stick) Mode 2(the throttle is the left stick) default Note: * There could be a few deviations due to manual measurement or slight color difference owing to photographing condition. Thank you for your understanding. Package Information: Package Size: 21.7 * 18.5 * 12.7cm Package Weight: 1044g Gift Box Package. Packing List: 1 * RC Aircraft 1 * Remote Controller 1 * Spare Propellers Set 1 * User Manual 1 * Drone Battery 1 * Charger 1 * USB Charging Cable 1 * Screwdriver.", + "features": [], + "images": [ + "https://images.barcodelookup.com/27019/270196129-1.jpg" + ], + "last_update": "2022-04-12 23:53:52", + "stores": [ + { + "name": "Walmart", + "country": "US", + "currency": "USD", + "currency_symbol": "$", + "price": "178.12", + "sale_price": "", + "tax": [], + "link": "https://www.walmart.com/ip/GoolRC-RC-Drone-with-Camera-4K-5G-Wifi-Brushless-Motor-GPS-Optical-Flow-Positioning-Track-Flight-RC-Quadcopter-for-Kids-and-Adults/789439849", + "item_group_id": "", + "availability": "", + "condition": "new", + "shipping": [], + "last_update": "2022-04-12 23:53:52" + } + ], + "reviews": [] + }, + { + "barcode_number": "854547170005", + "barcode_formats": "UPC-A 854547170005, EAN-13 0854547170005", + "mpn": "", + "model": "", + "asin": "", + "title": "Mad Hornets 10pcs UHF SO239 Female to MCX Male Coax Adapter Connector for GPS RADIO Antenna", + "category": "Electronics > Electronics Accessories", + "manufacturer": "Mad Hornets", + "brand": "", + "contributors": [], + "age_group": "", + "ingredients": "", + "nutrition_facts": "", + "energy_efficiency_class": "", + "color": "", + "gender": "", + "material": "", + "pattern": "", + "format": "", + "multipack": "", + "size": "", + "length": "", + "width": "", + "height": "", + "weight": "", + "release_date": "", + "description": "100% Brand New and High Quality. Shape: Same as the picture show 1. Type: UHF-MCX Adapter 2. Impedance: 50 ohm 3. Insulators: PTFE 4. Material: Brass 5. Connector A: UHF SO239 Female 6. Connector B: MCX Male 6. Plating: Nickel-plated 7. Connector Body Style: Straight Package included: 10pcs.", + "features": [], + "images": [ + "https://images.barcodelookup.com/27019/270196229-1.jpg" + ], + "last_update": "2023-04-07 08:26:04", + "stores": [ + { + "name": "Walmart", + "country": "US", + "currency": "USD", + "currency_symbol": "$", + "price": "34.89", + "sale_price": "34.89", + "tax": [], + "link": "https://www.walmart.com/ip/Mad-Hornets-10pcs-UHF-SO239-Female-to-MCX-Male-Coax-Adapter-Connector-For-GPS-RADIO-Antenna/648579009", + "item_group_id": "", + "availability": "", + "condition": "", + "shipping": [], + "last_update": "2023-04-07 08:26:04" + } + ], + "reviews": [] + }, + { + "barcode_number": "044122938605", + "barcode_formats": "UPC-A 044122938605, EAN-13 0044122938605", + "mpn": "", + "model": "", + "asin": "", + "title": "8Pcs Propellers for Mjx B7 Bugs 7 Hs510 Folding Gps Quadcopter 4K Drone Blade Kids Education Toys", + "category": "Toys & Games > Toys", + "manufacturer": "", + "brand": "", + "contributors": [], + "age_group": "", + "ingredients": "", + "nutrition_facts": "", + "energy_efficiency_class": "", + "color": "", + "gender": "", + "material": "", + "pattern": "", + "format": "", + "multipack": "", + "size": "", + "length": "", + "width": "", + "height": "", + "weight": "", + "release_date": "", + "description": "8PCS Propeller For MJX B7 Bugs 7 HS510 Folding GPS Quadcopter 4K Drone Blade Accessories Feature: 100% brand new and high quality High quality and durable in use Product size:110x15x7mm Propellers weight:8g(4pcs) Specially designed to provide for MJX B7 Bugs 7/ for Holy Stone HS510 GPS Drone Product Package: 8PCS propellers.", + "features": [], + "images": [ + "https://images.barcodelookup.com/27019/270196322-1.jpg" + ], + "last_update": "2022-12-25 14:21:59", + "stores": [ + { + "name": "Walmart", + "country": "US", + "currency": "USD", + "currency_symbol": "$", + "price": "30.19", + "sale_price": "24.19", + "tax": [], + "link": "https://www.walmart.com/ip/8Pcs-Propellers-For-Mjx-B7-Bugs-7-Hs510-Folding-Gps-Quadcopter-4K-Drone-Blade-Kids-Education-Toys/547393146", + "item_group_id": "", + "availability": "", + "condition": "", + "shipping": [], + "last_update": "2022-12-25 14:21:59" + } + ], + "reviews": [] + } + ] + } + } +} + + + + diff --git a/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_query_bad_key.json b/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_query_bad_key.json new file mode 100644 index 0000000000..8bfbf9b5dd --- /dev/null +++ b/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_query_bad_key.json @@ -0,0 +1,31 @@ +{ + "request": { + "urlPathPattern": "/barcodelookup/v3/products", + "method": "GET", + "queryParameters": { + "key": { + "not": { + "equalTo": "devKey" + } + }, + "search": { + "matches": ".*" + } + } + }, + "response": { + "status": 403, + "headers": { + "Content-Type": "application/json" + }, + "delayDistribution": { + "type": "lognormal", + "median": 500, + "sigma": 0.4 + } + } +} + + + + diff --git a/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_query_not_found.json b/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_query_not_found.json new file mode 100644 index 0000000000..6e84293b54 --- /dev/null +++ b/software/plugins/external-item-search/dev/services/mappings/barcodelookup/barcodelookup_query_not_found.json @@ -0,0 +1,32 @@ +{ + "request": { + "urlPathPattern": "/barcodelookup/v3/products", + "method": "GET", + "queryParameters": { + "key": { + "equalTo": "devKey" + }, + "search": { + "not": { + "equalTo": "GPS" + } + } + } + }, + "response": { + "status": 400, + "headers": { + "Content-Type": "application/json" + }, + "delayDistribution": { + "type": "lognormal", + "median": 3000, + "sigma": 0.4 + }, + "body": "\"\"" + } +} + + + + diff --git a/software/plugins/external-item-search/dev/services/mappings/datakick/datakick_barcode_not_found.json b/software/plugins/external-item-search/dev/services/mappings/datakick/datakick_barcode_not_found.json index 14f98ed207..463842703b 100644 --- a/software/plugins/external-item-search/dev/services/mappings/datakick/datakick_barcode_not_found.json +++ b/software/plugins/external-item-search/dev/services/mappings/datakick/datakick_barcode_not_found.json @@ -1,20 +1,20 @@ { - "request": { - "urlPathPattern": "/datakick/api/items/(?!00888109010058(?=/|$))$", - "method": "GET" - }, - "response": { - "status": 200, - "delayDistribution": { - "type": "lognormal", - "median": 5000, - "sigma": 0.4 - }, - "headers": { - "Content-Type": "application/json" - }, - "jsonBody": [] - } + "request": { + "urlPathPattern": "/datakick/api/items/(?!00888109010058(?=/|$))$", + "method": "GET" + }, + "response": { + "status": 200, + "delayDistribution": { + "type": "lognormal", + "median": 5000, + "sigma": 0.4 + }, + "headers": { + "Content-Type": "application/json" + }, + "jsonBody": [] + } } diff --git a/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part.json b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part_by_num.json similarity index 100% rename from software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part.json rename to software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part_by_num.json diff --git a/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part_bad_key.json b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part_by_num_bad_key.json similarity index 88% rename from software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part_bad_key.json rename to software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part_by_num_bad_key.json index b373639268..348a72be82 100644 --- a/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part_bad_key.json +++ b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part_by_num_bad_key.json @@ -4,7 +4,9 @@ "method": "GET", "headers": { "Authorization": { - "equalTo": "key devKeyBad" + "not": { + "equalTo": "key devKey" + } } } }, diff --git a/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part_not_found.json b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part_by_num_not_found.json similarity index 100% rename from software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part_not_found.json rename to software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_part_by_num_not_found.json diff --git a/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_parts_search.json b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_parts_search.json new file mode 100644 index 0000000000..b41de00c11 --- /dev/null +++ b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_parts_search.json @@ -0,0 +1,2232 @@ +{ + "request": { + "urlPathPattern": "/rebrickable/api/v3/lego/parts/", + "method": "GET", + "headers": { + "Authorization": { + "equalTo": "key devKey" + } + }, + "queryParameters": { + "search": { + "equalTo": "2x2 brick" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "delayDistribution": { + "type": "lognormal", + "median": 7000, + "sigma": 0.4 + }, + "jsonBody": { + "count": 1696, + "next": "https://rebrickable.com/api/v3/lego/parts/?page=2&search=2x2+brick", + "previous": null, + "results": [ + { + "part_num": "101349", + "name": "Duplo Light Brick 2 x 2 with White Button, Yellow Light", + "part_cat_id": 4, + "part_url": "https://rebrickable.com/parts/101349/duplo-light-brick-2-x-2-with-white-button-yellow-light/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6418211.jpg", + "external_ids": { + "BrickLink": [ + "101349c01" + ], + "BrickOwl": [ + "457087" + ], + "Brickset": [ + "101349" + ], + "LEGO": [ + "101349" + ] + } + }, + { + "part_num": "106872pr0001", + "name": "Minifig Head Special, Minion, Cone, Black 2-Eyed Goggles Pattern, Blue Cape, Yellow Mouth print", + "part_cat_id": 59, + "part_url": "https://rebrickable.com/parts/106872pr0001/minifig-head-special-minion-cone-black-2-eyed-goggles-pattern-blue-cape-yellow-mouth-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6485643.jpg", + "external_ids": { + "BrickLink": [ + "106872pb01" + ], + "BrickOwl": [ + "1350108" + ], + "Brickset": [ + "108465" + ], + "LEGO": [ + "108465" + ] + } + }, + { + "part_num": "10888", + "name": "Duplo Turntable 2 x 2", + "part_cat_id": 4, + "part_url": "https://rebrickable.com/parts/10888/duplo-turntable-2-x-2/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6022244.jpg", + "external_ids": { + "BrickLink": [ + "10564c01" + ], + "BrickOwl": [ + "293339" + ], + "Brickset": [ + "10888" + ], + "LEGO": [ + "10888" + ] + } + }, + { + "part_num": "11169", + "name": "Duplo Brick 2 x 2 x 1 1/2 with Curve", + "part_cat_id": 4, + "part_url": "https://rebrickable.com/parts/11169/duplo-brick-2-x-2-x-1-12-with-curve/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6094052.jpg", + "external_ids": { + "BrickLink": [ + "11169" + ], + "BrickOwl": [ + "821423" + ], + "Brickset": [ + "11169" + ], + "LDraw": [ + "11169" + ], + "LEGO": [ + "11169" + ] + } + }, + { + "part_num": "11170", + "name": "Duplo Brick 2 x 2 x 1 1/2 with Curved Top", + "part_cat_id": 4, + "part_url": "https://rebrickable.com/parts/11170/duplo-brick-2-x-2-x-1-12-with-curved-top/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6135521.jpg", + "external_ids": { + "BrickLink": [ + "11170" + ], + "BrickOwl": [ + "696944" + ], + "Brickset": [ + "11170" + ], + "LDraw": [ + "11170" + ], + "LEGO": [ + "11170" + ] + } + }, + { + "part_num": "11170pr0001", + "name": "Duplo Brick 2 x 2 x 1 1/2 with Curved Top, and '123' and 'REGULAR' Gas / Fuel / Petrol Pump Print", + "part_cat_id": 4, + "part_url": "https://rebrickable.com/parts/11170pr0001/duplo-brick-2-x-2-x-1-12-with-curved-top-and-123-and-regular-gas-fuel-petrol-pump-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6186590.jpg", + "external_ids": { + "BrickLink": [ + "11170pb03" + ], + "BrickOwl": [ + "910069" + ], + "Brickset": [ + "33346", + "68479" + ], + "LEGO": [ + "33346", + "68479" + ] + } + }, + { + "part_num": "11170pr0002", + "name": "Duplo Brick 2 x 2 x 1 1/2 with Curved Top and Silver Window Print on Both Sides", + "part_cat_id": 4, + "part_url": "https://rebrickable.com/parts/11170pr0002/duplo-brick-2-x-2-x-1-12-with-curved-top-and-silver-window-print-on-both-sides/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6020412.jpg", + "external_ids": { + "BrickLink": [ + "11170pb01" + ], + "BrickOwl": [ + "324669" + ], + "Brickset": [ + "12659" + ], + "LEGO": [ + "12659" + ] + } + }, + { + "part_num": "11170pr0003", + "name": "Duplo Brick 2 x 3 x 2 with Curved Top with Black Dots print", + "part_cat_id": 4, + "part_url": "https://rebrickable.com/parts/11170pr0003/duplo-brick-2-x-3-x-2-with-curved-top-with-black-dots-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6212276.jpg", + "external_ids": { + "BrickLink": [ + "11170pb04" + ], + "BrickOwl": [ + "494638" + ], + "Brickset": [ + "36514" + ], + "LEGO": [ + "36514" + ] + } + }, + { + "part_num": "11170pr0005", + "name": "Duplo Brick 2 x 2 x 1 1/2 with Curved Top, and Window with Girl and Boy on Opposite Sides Print", + "part_cat_id": 4, + "part_url": "https://rebrickable.com/parts/11170pr0005/duplo-brick-2-x-2-x-1-12-with-curved-top-and-window-with-girl-and-boy-on-opposite-sides-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6217858.jpg", + "external_ids": { + "BrickLink": [ + "11170pb02" + ], + "BrickOwl": [ + "907588" + ], + "Brickset": [ + "37342" + ], + "LEGO": [ + "37342" + ] + } + }, + { + "part_num": "11170pr0006", + "name": "Duplo Brick 2 x 2 x 1 1/2 with Curved Top Girl with Yellow Hair, Boy with Blue Baseball Cap Print", + "part_cat_id": 4, + "part_url": "https://rebrickable.com/parts/11170pr0006/duplo-brick-2-x-2-x-1-12-with-curved-top-girl-with-yellow-hair-boy-with-blue-baseball-cap-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6250605.jpg", + "external_ids": { + "BrickLink": [ + "11170pb05" + ], + "BrickOwl": [ + "213362" + ], + "Brickset": [ + "43441" + ], + "LEGO": [ + "43441" + ] + } + }, + { + "part_num": "11170pr0007", + "name": "Duplo Brick 2 x 2 x 1 1/2 with Curved Top with Boy with Red Fire Hat Print", + "part_cat_id": 4, + "part_url": "https://rebrickable.com/parts/11170pr0007/duplo-brick-2-x-2-x-1-12-with-curved-top-with-boy-with-red-fire-hat-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6250812.jpg", + "external_ids": { + "BrickLink": [ + "11170pb06" + ], + "BrickOwl": [ + "40845" + ], + "Brickset": [ + "43535" + ], + "LEGO": [ + "43535" + ] + } + }, + { + "part_num": "11170pr0009", + "name": "Duplo Brick 2 x 2 x 1 1/2 with Curved Top, Boy with Cap Print", + "part_cat_id": 4, + "part_url": "https://rebrickable.com/parts/11170pr0009/duplo-brick-2-x-2-x-1-12-with-curved-top-boy-with-cap-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6286339.jpg", + "external_ids": { + "BrickLink": [ + "11170pb09" + ], + "BrickOwl": [ + "414475" + ], + "Brickset": [ + "65979" + ], + "LDraw": [ + "11170p01" + ], + "LEGO": [ + "65979" + ] + } + }, + { + "part_num": "11170pr0010", + "name": "Duplo Brick 2 x 2 x 1 1/2 with Curved Top, Cash Register with '1.23' Print", + "part_cat_id": 4, + "part_url": "https://rebrickable.com/parts/11170pr0010/duplo-brick-2-x-2-x-1-12-with-curved-top-cash-register-with-123-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6295347.jpg", + "external_ids": { + "BrickLink": [ + "11170pb07" + ], + "BrickOwl": [ + "906498" + ], + "Brickset": [ + "67269" + ], + "LEGO": [ + "67269" + ] + } + }, + { + "part_num": "11170pr0011", + "name": "Duplo Brick 2 x 2 x 1 1/2 with Curved Top with Joker on Monitor Print", + "part_cat_id": 4, + "part_url": "https://rebrickable.com/parts/11170pr0011/duplo-brick-2-x-2-x-1-12-with-curved-top-with-joker-on-monitor-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6303084.jpg", + "external_ids": { + "BrickLink": [ + "11170pb08" + ], + "BrickOwl": [ + "217458" + ], + "Brickset": [ + "68277" + ], + "LEGO": [ + "68277" + ] + } + }, + { + "part_num": "11170pr0013", + "name": "Duplo Brick 2 x 2 x 1 1/2 with Curved Top with Rainbow on Left Side Print", + "part_cat_id": 4, + "part_url": "https://rebrickable.com/parts/11170pr0013/duplo-brick-2-x-2-x-1-12-with-curved-top-with-rainbow-on-left-side-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6330516.jpg", + "external_ids": { + "BrickLink": [ + "11170pb10R" + ], + "BrickOwl": [ + "779647" + ], + "Brickset": [ + "74969" + ], + "LEGO": [ + "74969" + ] + } + }, + { + "part_num": "11170pr0014", + "name": "Duplo Brick 2 x 2 x 1 1/2 with Curved Top with Rainbow on Right Side Print", + "part_cat_id": 4, + "part_url": "https://rebrickable.com/parts/11170pr0014/duplo-brick-2-x-2-x-1-12-with-curved-top-with-rainbow-on-right-side-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6330517.jpg", + "external_ids": { + "BrickLink": [ + "11170pb10L" + ], + "BrickOwl": [ + "339505" + ], + "Brickset": [ + "74980" + ], + "LEGO": [ + "74980" + ] + } + }, + { + "part_num": "11170pr0015", + "name": "Duplo Brick 2 x 2 x 1 1/2 with Curved Top with Policeman on Left, Policewoman on Right Print", + "part_cat_id": 4, + "part_url": "https://rebrickable.com/parts/11170pr0015/duplo-brick-2-x-2-x-1-12-with-curved-top-with-policeman-on-left-policewoman-on-right-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6334459.jpg", + "external_ids": { + "BrickLink": [ + "11170pb12" + ], + "BrickOwl": [ + "405434" + ], + "Brickset": [ + "76788" + ], + "LEGO": [ + "76788" + ] + } + }, + { + "part_num": "11170pr0016", + "name": "Duplo Brick 2 x 2 x 1 1/2 with Curved Top with Criminal on Left, Dog on Right Print", + "part_cat_id": 4, + "part_url": "https://rebrickable.com/parts/11170pr0016/duplo-brick-2-x-2-x-1-12-with-curved-top-with-criminal-on-left-dog-on-right-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6334461.jpg", + "external_ids": { + "BrickLink": [ + "11170pb13" + ], + "BrickOwl": [ + "992925" + ], + "Brickset": [ + "76789" + ], + "LEGO": [ + "76789" + ] + } + }, + { + "part_num": "11170pr0017", + "name": "Duplo Brick 2 x 2 x 1 1/2 with Curved Top with Firefighters, Male on Right, Female on Left Print", + "part_cat_id": 4, + "part_url": "https://rebrickable.com/parts/11170pr0017/duplo-brick-2-x-2-x-1-12-with-curved-top-with-firefighters-male-on-right-female-on-left-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6336207.jpg", + "external_ids": { + "BrickLink": [ + "11170pb14" + ], + "BrickOwl": [ + "520672" + ], + "Brickset": [ + "77142" + ], + "LEGO": [ + "77142" + ] + } + }, + { + "part_num": "11170pr0019", + "name": "Duplo Brick 2 x 2 x 1 1/2 with Curved Top with Yellow Sun print", + "part_cat_id": 4, + "part_url": "https://rebrickable.com/parts/11170pr0019/duplo-brick-2-x-2-x-1-12-with-curved-top-with-yellow-sun-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6420867.jpg", + "external_ids": { + "BrickLink": [ + "11170pb15R" + ], + "BrickOwl": [ + "1005419" + ], + "Brickset": [ + "101560" + ], + "LEGO": [ + "101560" + ] + } + }, + { + "part_num": "11170pr0020", + "name": "Duplo Brick 2 x 2 x 1 1/2 with Curved Top with Yellow Sun print", + "part_cat_id": 4, + "part_url": "https://rebrickable.com/parts/11170pr0020/duplo-brick-2-x-2-x-1-12-with-curved-top-with-yellow-sun-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6420868.jpg", + "external_ids": { + "BrickLink": [ + "11170pb15L" + ], + "BrickOwl": [ + "1295396" + ], + "Brickset": [ + "101561" + ], + "LEGO": [ + "101561" + ] + } + }, + { + "part_num": "11170pr0021", + "name": "Duplo Brick 2 x 2 x 1 1/2 with Curved Top with Rainbow on Left Side, Rain Cloud on Right Side Print", + "part_cat_id": 4, + "part_url": "https://rebrickable.com/parts/11170pr0021/duplo-brick-2-x-2-x-1-12-with-curved-top-with-rainbow-on-left-side-rain-cloud-on-right-side-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6440239.jpg", + "external_ids": { + "BrickLink": [ + "11170pb16R" + ], + "BrickOwl": [ + "1041677" + ], + "Brickset": [ + "103923" + ], + "LEGO": [ + "103923" + ] + } + }, + { + "part_num": "11170pr0022", + "name": "Duplo Brick 2 x 2 x 1 1/2 with Curved Top with Rainbow on Right Side, Rain Cloud on Left Side Print", + "part_cat_id": 4, + "part_url": "https://rebrickable.com/parts/11170pr0022/duplo-brick-2-x-2-x-1-12-with-curved-top-with-rainbow-on-right-side-rain-cloud-on-left-side-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6440240.jpg", + "external_ids": { + "BrickLink": [ + "11170pb16L" + ], + "BrickOwl": [ + "1265514" + ], + "Brickset": [ + "103924" + ], + "LEGO": [ + "103924" + ] + } + }, + { + "part_num": "11170pr0023", + "name": "Duplo Brick 2 x 2 x 1 1/2 with Control Screen, Lightning Bolt, Leave, train, Shapes print", + "part_cat_id": 4, + "part_url": "https://rebrickable.com/parts/11170pr0023/duplo-brick-2-x-2-x-1-12-with-control-screen-lightning-bolt-leave-train-shapes-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6478084.jpg", + "external_ids": { + "BrickLink": [ + "11170pb17" + ], + "BrickOwl": [ + "634008" + ], + "Brickset": [ + "107390" + ], + "LEGO": [ + "107390" + ] + } + }, + { + "part_num": "11215", + "name": "Bracket 5 x 2 x 1 1/3 with Holes, Pin Bottom", + "part_cat_id": 5, + "part_url": "https://rebrickable.com/parts/11215/bracket-5-x-2-x-1-13-with-holes-pin-bottom/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6028811.jpg", + "external_ids": { + "BrickLink": [ + "11215" + ], + "BrickOwl": [ + "600784" + ], + "Brickset": [ + "11215" + ], + "LDraw": [ + "11215" + ], + "LEGO": [ + "11215" + ] + } + }, + { + "part_num": "1122L", + "name": "Modulex Brick 2 x 2 with LEGO on Studs", + "part_cat_id": 66, + "part_url": "https://rebrickable.com/parts/1122L/modulex-brick-2-x-2-with-lego-on-studs/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/photos/1013/1122L-1013-14c007b7-6825-476c-b540-95447225003c.jpg", + "external_ids": { + "BrickLink": [ + "Mx1122L" + ], + "BrickOwl": [ + "107135" + ] + } + }, + { + "part_num": "1122M", + "name": "Modulex Brick 2 x 2 with M on Studs", + "part_cat_id": 66, + "part_url": "https://rebrickable.com/parts/1122M/modulex-brick-2-x-2-with-m-on-studs/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/photos/1035/1122M-1035-44ab9a89-b223-4e24-8019-e47d733c6f8e.jpg", + "external_ids": { + "BrickLink": [ + "Mx1122M" + ], + "BrickOwl": [ + "357369" + ], + "LDraw": [ + "u7006" + ] + } + }, + { + "part_num": "1140Ma", + "name": "Modulex Brick 2 x 20 with M on Studs", + "part_cat_id": 66, + "part_url": "https://rebrickable.com/parts/1140Ma/modulex-brick-2-x-20-with-m-on-studs/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/photos/1030/1140Ma-1030-2f4c61c5-b6f4-4620-b13b-04a9ddb9a65f.jpg", + "external_ids": { + "BrickLink": [ + "Mx1140AM" + ], + "BrickOwl": [ + "546989" + ], + "LDraw": [ + "u7011" + ] + } + }, + { + "part_num": "1140Mb", + "name": "Modulex Brick 2 x 20 with M on Studs - No End Walls", + "part_cat_id": 66, + "part_url": "https://rebrickable.com/parts/1140Mb/modulex-brick-2-x-20-with-m-on-studs-no-end-walls/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/photos/1022/1140Mb-1022-524cfd75-4266-4887-a2bf-bce8015dc86d.jpg", + "external_ids": { + "BrickLink": [ + "Mx1140BM" + ], + "BrickOwl": [ + "451665" + ], + "LDraw": [ + "u7027" + ] + } + }, + { + "part_num": "114476c01", + "name": "Duplo Brick, 2x2 with Black Hose and Nozzle", + "part_cat_id": 4, + "part_url": "https://rebrickable.com/parts/114476c01/duplo-brick-2x2-with-black-hose-and-nozzle/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6563752.jpg", + "external_ids": { + "BrickLink": [ + "114476c01" + ], + "BrickOwl": [ + "1371469" + ], + "Brickset": [ + "114476" + ], + "LEGO": [ + "114476" + ] + } + }, + { + "part_num": "13548", + "name": "Wedge Sloped 45� 2 x 2 Corner", + "part_cat_id": 6, + "part_url": "https://rebrickable.com/parts/13548/wedge-sloped-45-2-x-2-corner/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6227909.jpg", + "external_ids": { + "BrickLink": [ + "13548" + ], + "BrickOwl": [ + "7021" + ], + "Brickset": [ + "13548" + ], + "LDraw": [ + "13548" + ], + "LEGO": [ + "13548" + ] + } + }, + { + "part_num": "13548pr0001", + "name": "Wedge Sloped 45� 2 x 2 Corner with White Dots, Circles print", + "part_cat_id": 6, + "part_url": "https://rebrickable.com/parts/13548pr0001/wedge-sloped-45-2-x-2-corner-with-white-dots-circles-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6245200.jpg", + "external_ids": { + "BrickLink": [ + "13548pb001" + ], + "BrickOwl": [ + "202219" + ], + "Brickset": [ + "42046" + ], + "LDraw": [ + "13548pz1" + ], + "LEGO": [ + "42046" + ] + } + }, + { + "part_num": "14769pr1154", + "name": "Tile Round 2 x 2 with White and Black Circle, 2 White Squares (Brickheadz Eye) print", + "part_cat_id": 67, + "part_url": "https://rebrickable.com/parts/14769pr1154/tile-round-2-x-2-with-white-and-black-circle-2-white-squares-brickheadz-eye-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6313877.jpg", + "external_ids": { + "BrickLink": [ + "14769pb416" + ], + "BrickOwl": [ + "21312" + ], + "Brickset": [ + "68956" + ], + "LDraw": [ + "14769pz1" + ], + "LEGO": [ + "68956" + ] + } + }, + { + "part_num": "14769pr1187", + "name": "Tile Round 2 x 2 with Brickheadz Eye print", + "part_cat_id": 67, + "part_url": "https://rebrickable.com/parts/14769pr1187/tile-round-2-x-2-with-brickheadz-eye-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6345693.jpg", + "external_ids": { + "BrickLink": [ + "14769pb417" + ], + "BrickOwl": [ + "356560" + ], + "Brickset": [ + "78557" + ], + "LDraw": [ + "14769ph0" + ], + "LEGO": [ + "78557" + ] + } + }, + { + "part_num": "14769pr1238", + "name": "Tile Round 2 x 2 with Blue Brickheadz Eye, Pearl Gold Border print", + "part_cat_id": 67, + "part_url": "https://rebrickable.com/parts/14769pr1238/tile-round-2-x-2-with-blue-brickheadz-eye-pearl-gold-border-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6386819.jpg", + "external_ids": { + "BrickLink": [ + "14769pb536" + ], + "BrickOwl": [ + "19809" + ], + "Brickset": [ + "1388" + ], + "LDraw": [ + "14769ph1" + ], + "LEGO": [ + "1388" + ] + } + }, + { + "part_num": "14769pr1251", + "name": "Tile Round 2 x 2 with White Squares (Brickheadz Eye) print", + "part_cat_id": 67, + "part_url": "https://rebrickable.com/parts/14769pr1251/tile-round-2-x-2-with-white-squares-brickheadz-eye-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6430739.jpg", + "external_ids": { + "BrickLink": [ + "14769pb583" + ], + "BrickOwl": [ + "1080359" + ], + "Brickset": [ + "102469" + ], + "LDraw": [ + "14769pz0" + ], + "LEGO": [ + "102469" + ] + } + }, + { + "part_num": "14769pr9980", + "name": "Tile Round 2 x 2 with Dark Azure Brickheadz Eye, Black Pupil print", + "part_cat_id": 67, + "part_url": "https://rebrickable.com/parts/14769pr9980/tile-round-2-x-2-with-dark-azure-brickheadz-eye-black-pupil-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6498282.jpg", + "external_ids": { + "BrickLink": [ + "14769pb702L" + ], + "BrickOwl": [ + "1152907" + ], + "Brickset": [ + "109505" + ], + "LDraw": [ + "14769pz2" + ], + "LEGO": [ + "109505" + ] + } + }, + { + "part_num": "14769pr9981", + "name": "Tile Round 2 x 2 with Dark Azure Brickheadz Eye, Black Pupil print", + "part_cat_id": 67, + "part_url": "https://rebrickable.com/parts/14769pr9981/tile-round-2-x-2-with-dark-azure-brickheadz-eye-black-pupil-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6493597.jpg", + "external_ids": { + "BrickLink": [ + "14769pb702R" + ], + "BrickOwl": [ + "160660" + ], + "Brickset": [ + "109426" + ], + "LDraw": [ + "14769pz3" + ], + "LEGO": [ + "109426" + ] + } + }, + { + "part_num": "14769pr9985", + "name": "Tile Round 2 x 2 with Brickheadz Eye print", + "part_cat_id": 67, + "part_url": "https://rebrickable.com/parts/14769pr9985/tile-round-2-x-2-with-brickheadz-eye-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6492707.jpg", + "external_ids": { + "BrickLink": [ + "14769pb701" + ], + "BrickOwl": [ + "176737" + ], + "Brickset": [ + "109375" + ], + "LEGO": [ + "109375" + ] + } + }, + { + "part_num": "1486c01", + "name": "Duplo Sound Brick 2 x 2 with White Button", + "part_cat_id": 4, + "part_url": "https://rebrickable.com/parts/1486c01/duplo-sound-brick-2-x-2-with-white-button/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6374790.jpg", + "external_ids": { + "BrickLink": [ + "1486c01" + ], + "BrickOwl": [ + "1366684" + ], + "Brickset": [ + "84288" + ], + "LEGO": [ + "84288" + ] + } + }, + { + "part_num": "15068", + "name": "Slope Curved 2 x 2 x 2/3", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068/slope-curved-2-x-2-x-23/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6053077.jpg", + "external_ids": { + "BrickLink": [ + "15068" + ], + "BrickOwl": [ + "593875" + ], + "Brickset": [ + "15068", + "78565" + ], + "LDraw": [ + "15068" + ], + "LEGO": [ + "15068", + "78565" + ] + } + }, + { + "part_num": "15068pr0001", + "name": "Slope Curved 2 x 2 x 2/3 with Lion Legend Beast Eyes and Crest Print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0001/slope-curved-2-x-2-x-23-with-lion-legend-beast-eyes-and-crest-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6051328.jpg", + "external_ids": { + "BrickLink": [ + "15068pb002" + ], + "BrickOwl": [ + "711214" + ], + "Brickset": [ + "15518" + ], + "LEGO": [ + "15518" + ] + } + }, + { + "part_num": "15068pr0002", + "name": "Slope Curved 2 x 2 x 2/3 with Wolf Legend Beast Eyes and Crest Print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0002/slope-curved-2-x-2-x-23-with-wolf-legend-beast-eyes-and-crest-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6054628.jpg", + "external_ids": { + "BrickLink": [ + "15068pb001" + ], + "BrickOwl": [ + "268821" + ], + "Brickset": [ + "15716" + ], + "LDraw": [ + "15068p01" + ], + "LEGO": [ + "15716" + ] + } + }, + { + "part_num": "15068pr0003", + "name": "Slope Curved 2 x 2 x 2/3 with Arctic Explorer Logo Print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0003/slope-curved-2-x-2-x-23-with-arctic-explorer-logo-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6068073.jpg", + "external_ids": { + "BrickLink": [ + "15068pb004" + ], + "BrickOwl": [ + "423881" + ], + "Brickset": [ + "17194" + ], + "LDraw": [ + "15068p03" + ], + "LEGO": [ + "17194" + ] + } + }, + { + "part_num": "15068pr0004", + "name": "Slope Curved 2 x 2 x 2/3 with Dark Tan Fur Print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0004/slope-curved-2-x-2-x-23-with-dark-tan-fur-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6079133.jpg", + "external_ids": { + "BrickLink": [ + "15068pb010" + ], + "BrickOwl": [ + "39344" + ], + "Brickset": [ + "18136" + ], + "LDraw": [ + "15068p02" + ], + "LEGO": [ + "18136" + ] + } + }, + { + "part_num": "15068pr0005", + "name": "Slope Curved 2 x 2 x 2/3 with Ghostbusters Print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0005/slope-curved-2-x-2-x-23-with-ghostbusters-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6092268.jpg", + "external_ids": { + "BrickLink": [ + "15068pb005" + ], + "BrickOwl": [ + "576313" + ], + "Brickset": [ + "18871" + ], + "LDraw": [ + "15068p04" + ], + "LEGO": [ + "18871" + ] + } + }, + { + "part_num": "15068pr0006", + "name": "Slope Curved 2 x 2 x 2/3 with Fire on Black Badge print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0006/slope-curved-2-x-2-x-23-with-fire-on-black-badge-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6132565.jpg", + "external_ids": { + "BrickLink": [ + "15068pb052" + ], + "BrickOwl": [ + "590710" + ], + "Brickset": [ + "24410" + ], + "LDraw": [ + "15068p09" + ], + "LEGO": [ + "24410" + ] + } + }, + { + "part_num": "15068pr0007", + "name": "Slope Curved 2 x 2 x 2/3 with Badge and Blue 'POLICE' Text print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0007/slope-curved-2-x-2-x-23-with-badge-and-blue-police-text-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6132740.jpg", + "external_ids": { + "BrickLink": [ + "15068pb046b" + ], + "BrickOwl": [ + "979066" + ], + "Brickset": [ + "24437" + ], + "LEGO": [ + "24437" + ] + } + }, + { + "part_num": "15068pr0008", + "name": "Slope Curved 2 x 2 x 2/3 with Fancy Scroll Print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0008/slope-curved-2-x-2-x-23-with-fancy-scroll-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6135348.jpg", + "external_ids": { + "BrickLink": [ + "15068pb047" + ], + "BrickOwl": [ + "527838" + ], + "Brickset": [ + "24894" + ], + "LDraw": [ + "15068px1" + ], + "LEGO": [ + "24894" + ] + } + }, + { + "part_num": "15068pr0009", + "name": "Slope Curved 2 x 2 x 2/3 with Compass print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0009/slope-curved-2-x-2-x-23-with-compass-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6152204.jpg", + "external_ids": { + "BrickLink": [ + "15068pb084" + ], + "BrickOwl": [ + "959064" + ], + "Brickset": [ + "26686" + ], + "LEGO": [ + "26686" + ] + } + }, + { + "part_num": "15068pr0010", + "name": "Slope Curved 2 x 2 x 2/3 with Red, Light Bluish Gray and Black Stripes Print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0010/slope-curved-2-x-2-x-23-with-red-light-bluish-gray-and-black-stripes-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6156951.jpg", + "external_ids": { + "BrickLink": [ + "15068pb075" + ], + "BrickOwl": [ + "191853" + ], + "Brickset": [ + "27196" + ], + "LDraw": [ + "15068p0g" + ], + "LEGO": [ + "27196" + ] + } + }, + { + "part_num": "15068pr0011", + "name": "Slope Curved 2 x 2 x 2/3 with Jake the Dog Face Print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0011/slope-curved-2-x-2-x-23-with-jake-the-dog-face-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6159514.jpg", + "external_ids": { + "BrickLink": [ + "15068pb091" + ], + "BrickOwl": [ + "374260" + ], + "Brickset": [ + "27461" + ], + "LDraw": [ + "15068p0i" + ], + "LEGO": [ + "27461" + ] + } + }, + { + "part_num": "15068pr0012", + "name": "Slope Curved 2 x 2 x 2/3 with Dark Blue Eyes and Azure Lines print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0012/slope-curved-2-x-2-x-23-with-dark-blue-eyes-and-azure-lines-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6176583.jpg", + "external_ids": { + "BrickLink": [ + "15068pb118" + ], + "BrickOwl": [ + "996187" + ], + "Brickset": [ + "29951" + ], + "LDraw": [ + "15068px0" + ], + "LEGO": [ + "29951" + ] + } + }, + { + "part_num": "15068pr0013", + "name": "Slope Curved 2 x 2 x 2/3 with Gray Shield Emblem with Bright Light Orange Bull Inset Print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0013/slope-curved-2-x-2-x-23-with-gray-shield-emblem-with-bright-light-orange-bull-inset-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6172169.jpg", + "external_ids": { + "BrickLink": [ + "15068pb105" + ], + "BrickOwl": [ + "553368" + ], + "Brickset": [ + "29091" + ], + "LEGO": [ + "29091" + ] + } + }, + { + "part_num": "15068pr0014", + "name": "Slope Curved 2 x 2 x 2/3 with 'MUSEUM' Print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0014/slope-curved-2-x-2-x-23-with-museum-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6182352.jpg", + "external_ids": { + "BrickLink": [ + "15068pb110" + ], + "BrickOwl": [ + "629438" + ], + "Brickset": [ + "32641" + ], + "LDraw": [ + "15068p05" + ], + "LEGO": [ + "32641" + ] + } + }, + { + "part_num": "15068pr0015", + "name": "Slope Curved 2 x 2 x 2/3 with Badge and Blue 'POLICE' Text print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0015/slope-curved-2-x-2-x-23-with-badge-and-blue-police-text-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6174872.jpg", + "external_ids": { + "BrickLink": [ + "15068pb046a" + ], + "BrickOwl": [ + "864038" + ], + "Brickset": [ + "29649" + ], + "LEGO": [ + "29649" + ] + } + }, + { + "part_num": "15068pr0016", + "name": "Slope Curved 2 x 2 x 2/3 with Toad Face print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0016/slope-curved-2-x-2-x-23-with-toad-face-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6523129.jpg", + "external_ids": { + "BrickLink": [ + "15068pb596" + ], + "BrickOwl": [ + "9489" + ], + "Brickset": [ + "110767" + ], + "LEGO": [ + "110767" + ] + } + }, + { + "part_num": "15068pr0017", + "name": "Slope Curved 2 x 2 x 2/3 with Silver '7' in Circles over Black Stripes print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0017/slope-curved-2-x-2-x-23-with-silver-7-in-circles-over-black-stripes-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6181526.jpg", + "external_ids": { + "BrickLink": [ + "15068pb085" + ], + "BrickOwl": [ + "184414" + ], + "Brickset": [ + "31902" + ], + "LDraw": [ + "15068pt0" + ], + "LEGO": [ + "31902" + ] + } + }, + { + "part_num": "15068pr0018", + "name": "Slope Curved 2 x 2 x 2/3 with Catwoman Catcycle Print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0018/slope-curved-2-x-2-x-23-with-catwoman-catcycle-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6172456.jpg", + "external_ids": { + "BrickLink": [ + "15068pb094" + ], + "BrickOwl": [ + "426362" + ], + "Brickset": [ + "29133" + ], + "LDraw": [ + "15068pb0" + ], + "LEGO": [ + "29133" + ] + } + }, + { + "part_num": "15068pr0019", + "name": "Slope Curved 2 x 2 x 2/3 with Coast Guard Logo with Blue Waves and Life Preserver Print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0019/slope-curved-2-x-2-x-23-with-coast-guard-logo-with-blue-waves-and-life-preserver-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6183758.jpg", + "external_ids": { + "BrickLink": [ + "15068pb122" + ], + "BrickOwl": [ + "818698" + ], + "Brickset": [ + "32772" + ], + "LEGO": [ + "32772" + ] + } + }, + { + "part_num": "15068pr0020", + "name": "Slope Curved 2 x 2 x 2/3 with Skeleton Mouth and Nose", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0020/slope-curved-2-x-2-x-23-with-skeleton-mouth-and-nose/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6209739.jpg", + "external_ids": { + "BrickLink": [ + "15068pb127" + ], + "BrickOwl": [ + "74095" + ], + "Brickset": [ + "36023" + ], + "LEGO": [ + "36023" + ] + } + }, + { + "part_num": "15068pr0021", + "name": "Slope Curved 2 x 2 x 2/3 with Red and White Circles Print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0021/slope-curved-2-x-2-x-23-with-red-and-white-circles-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6195344.jpg", + "external_ids": { + "BrickLink": [ + "15068pb129" + ], + "BrickOwl": [ + "497154" + ], + "Brickset": [ + "34429" + ], + "LEGO": [ + "34429" + ] + } + }, + { + "part_num": "15068pr0022", + "name": "Slope Curved 2 x 2 x 2/3 with 2 Purple Squares Print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0022/slope-curved-2-x-2-x-23-with-2-purple-squares-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6195345.jpg", + "external_ids": { + "BrickLink": [ + "15068pb128" + ], + "BrickOwl": [ + "679843" + ], + "Brickset": [ + "34431" + ], + "LEGO": [ + "34431" + ] + } + }, + { + "part_num": "15068pr0023", + "name": "Slope Curved 2 x 2 x 2/3 with Darth Vader Control Panel, Buttons, Lights print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0023/slope-curved-2-x-2-x-23-with-darth-vader-control-panel-buttons-lights-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6209953.jpg", + "external_ids": { + "BrickLink": [ + "15068pb130R" + ], + "BrickOwl": [ + "309856" + ], + "Brickset": [ + "36082" + ], + "LEGO": [ + "36082" + ] + } + }, + { + "part_num": "15068pr0024", + "name": "Slope Curved 2 x 2 x 2/3 with Darth Vader Control Panel, Buttons, Lights print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0024/slope-curved-2-x-2-x-23-with-darth-vader-control-panel-buttons-lights-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6209954.jpg", + "external_ids": { + "BrickLink": [ + "15068pb130L" + ], + "BrickOwl": [ + "386907" + ], + "Brickset": [ + "36085" + ], + "LEGO": [ + "36085" + ] + } + }, + { + "part_num": "15068pr0025", + "name": "Slope Curved 2 x 2 x 2/3 with Black/Dark Bluish Grey Hatch print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0025/slope-curved-2-x-2-x-23-with-blackdark-bluish-grey-hatch-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6524575.jpg", + "external_ids": { + "BrickLink": [ + "15068pb603" + ], + "BrickOwl": [ + "395280" + ], + "Brickset": [ + "111180" + ], + "LEGO": [ + "111180" + ] + } + }, + { + "part_num": "15068pr0026", + "name": "Slope Curved 2 x 2 x 2/3 with Black Rectangle, Line print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0026/slope-curved-2-x-2-x-23-with-black-rectangle-line-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6523390.jpg", + "external_ids": { + "BrickLink": [ + "15068pb604" + ], + "BrickOwl": [ + "1259934" + ], + "Brickset": [ + "110962" + ], + "LEGO": [ + "110962" + ] + } + }, + { + "part_num": "15068pr0027", + "name": "Slope Curved 2 x 2 x 2/3 with Bullet Hole, Scratch print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0027/slope-curved-2-x-2-x-23-with-bullet-hole-scratch-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6224529.jpg", + "external_ids": { + "BrickLink": [ + "15068pb153" + ], + "BrickOwl": [ + "706980" + ], + "Brickset": [ + "38406" + ], + "LDraw": [ + "15068pz1" + ], + "LEGO": [ + "38406" + ] + } + }, + { + "part_num": "15068pr0028", + "name": "Slope Curved 2 x 2 x 2/3 with Black '8', Lime Octagons print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0028/slope-curved-2-x-2-x-23-with-black-8-lime-octagons-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6588834.jpg", + "external_ids": { + "BrickLink": [ + "15068pb681" + ], + "BrickOwl": [ + "1456957" + ], + "Brickset": [ + "115995" + ], + "LEGO": [ + "115995" + ] + } + }, + { + "part_num": "15068pr0029", + "name": "Slope Curved 2 x 2 x 2/3 with Stormtrooper Breathing Mask print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0029/slope-curved-2-x-2-x-23-with-stormtrooper-breathing-mask-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6236615.jpg", + "external_ids": { + "BrickLink": [ + "15068pb146" + ], + "BrickOwl": [ + "398989" + ], + "Brickset": [ + "39610" + ], + "LDraw": [ + "15068pz0" + ], + "LEGO": [ + "39610" + ] + } + }, + { + "part_num": "15068pr0030", + "name": "Slope Curved 2 x 2 x 2/3 with Yellow/Gold Scales", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0030/slope-curved-2-x-2-x-23-with-yellowgold-scales/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6258955.jpg", + "external_ids": { + "BrickLink": [ + "15068pb161L" + ], + "BrickOwl": [ + "469305" + ], + "Brickset": [ + "50486" + ], + "LEGO": [ + "50486" + ] + } + }, + { + "part_num": "15068pr0031", + "name": "Slope Curved 2 x 2 x 2/3 with Yellow/Gold Scales", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0031/slope-curved-2-x-2-x-23-with-yellowgold-scales/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6258953.jpg", + "external_ids": { + "BrickLink": [ + "15068pb161R" + ], + "BrickOwl": [ + "331523" + ], + "Brickset": [ + "50493" + ], + "LEGO": [ + "50493" + ] + } + }, + { + "part_num": "15068pr0032", + "name": "Slope Curved 2 x 2 x 2/3 with 2 White Stripes/Lines print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0032/slope-curved-2-x-2-x-23-with-2-white-stripeslines-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6258695.jpg", + "external_ids": { + "BrickLink": [ + "15068pb163" + ], + "BrickOwl": [ + "817575" + ], + "Brickset": [ + "45351" + ], + "LDraw": [ + "15068p07" + ], + "LEGO": [ + "45351" + ] + } + }, + { + "part_num": "15068pr0033", + "name": "Slope Curved 2 x 2 x 2/3 with dark Blue Lines/Stripe print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0033/slope-curved-2-x-2-x-23-with-dark-blue-linesstripe-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6258166.jpg", + "external_ids": { + "BrickLink": [ + "15068pb164" + ], + "BrickOwl": [ + "542338" + ], + "Brickset": [ + "45354" + ], + "LDraw": [ + "15068p06" + ], + "LEGO": [ + "45354" + ] + } + }, + { + "part_num": "15068pr0034", + "name": "Slope Curved 2 x 2 x 2/3 with Black Hexagon Pattern, Red Flames print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0034/slope-curved-2-x-2-x-23-with-black-hexagon-pattern-red-flames-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6266986.jpg", + "external_ids": { + "BrickLink": [ + "15068pb195" + ], + "BrickOwl": [ + "830803" + ], + "Brickset": [ + "52882" + ], + "LEGO": [ + "52882" + ] + } + }, + { + "part_num": "15068pr0035", + "name": "Slope Curved 2 x 2 x 2/3 with Open Mouth, Pink Tongue print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0035/slope-curved-2-x-2-x-23-with-open-mouth-pink-tongue-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6597295.jpg", + "external_ids": { + "BrickLink": [ + "15068pb682" + ], + "BrickOwl": [ + "1349068" + ], + "Brickset": [ + "116655" + ], + "LEGO": [ + "116655" + ] + } + }, + { + "part_num": "15068pr0038", + "name": "Slope Curved 2 x 2 x 2/3 with Yellow Fire Fighter Symbol Print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0038/slope-curved-2-x-2-x-23-with-yellow-fire-fighter-symbol-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6288042.jpg", + "external_ids": { + "BrickLink": [ + "15068pb224" + ], + "BrickOwl": [ + "20033" + ], + "Brickset": [ + "66210" + ], + "LEGO": [ + "66210" + ] + } + }, + { + "part_num": "15068pr0039", + "name": "Slope Curved 2 x 2 x 2/3 with Dark Tan/Dark Red Ruffles (Dragon) print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0039/slope-curved-2-x-2-x-23-with-dark-tandark-red-ruffles-dragon-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6298827.jpg", + "external_ids": { + "BrickLink": [ + "15068pb225" + ], + "BrickOwl": [ + "96258" + ], + "Brickset": [ + "67526" + ], + "LDraw": [ + "15068p0c" + ], + "LEGO": [ + "67526" + ] + } + }, + { + "part_num": "15068pr0040", + "name": "Slope Curved 2 x 2 x 2/3 with Dark Tan/Dark Red Ruffles (Dragon) print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0040/slope-curved-2-x-2-x-23-with-dark-tandark-red-ruffles-dragon-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6298834.jpg", + "external_ids": { + "BrickLink": [ + "15068pb227" + ], + "BrickOwl": [ + "160093" + ], + "Brickset": [ + "67525" + ], + "LDraw": [ + "15068p0e" + ], + "LEGO": [ + "67525" + ] + } + }, + { + "part_num": "15068pr0041", + "name": "Slope Curved 2 x 2 x 2/3 with Dark Tan/Yellow Ruffles (Dragon) print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0041/slope-curved-2-x-2-x-23-with-dark-tanyellow-ruffles-dragon-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6298835.jpg", + "external_ids": { + "BrickLink": [ + "15068pb226" + ], + "BrickOwl": [ + "262418" + ], + "Brickset": [ + "67523" + ], + "LDraw": [ + "15068p0d" + ], + "LEGO": [ + "67523" + ] + } + }, + { + "part_num": "15068pr0042", + "name": "Slope Curved 2 x 2 x 2/3 with Black Tentacles Print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0042/slope-curved-2-x-2-x-23-with-black-tentacles-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6302176.jpg", + "external_ids": { + "BrickLink": [ + "15068pb269" + ], + "BrickOwl": [ + "899522" + ], + "Brickset": [ + "68086" + ], + "LEGO": [ + "68086" + ] + } + }, + { + "part_num": "15068pr0043", + "name": "Slope Curved 2 x 2 x 2/3 with Star of Life and 'Beach Rescue' Print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0043/slope-curved-2-x-2-x-23-with-star-of-life-and-beach-rescue-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6326438.jpg", + "external_ids": { + "BrickLink": [ + "15068pb315" + ], + "BrickOwl": [ + "721387" + ], + "Brickset": [ + "73122" + ], + "LEGO": [ + "73122" + ] + } + }, + { + "part_num": "15068pr0044", + "name": "Slope Curved 2 x 2 x 2/3 with White Panel and Black and Red Detailing Print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0044/slope-curved-2-x-2-x-23-with-white-panel-and-black-and-red-detailing-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6323903.jpg", + "external_ids": { + "BrickLink": [ + "15068pb366" + ], + "BrickOwl": [ + "549538" + ], + "Brickset": [ + "72324" + ], + "LEGO": [ + "72324" + ] + } + }, + { + "part_num": "15068pr0045", + "name": "Slope Curved 2 x 2 x 2/3 with 'targa' print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0045/slope-curved-2-x-2-x-23-with-targa-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6342998.jpg", + "external_ids": { + "BrickLink": [ + "15068pb332" + ], + "BrickOwl": [ + "81816" + ], + "Brickset": [ + "78283" + ], + "LDraw": [ + "15068pt1" + ], + "LEGO": [ + "78283" + ] + } + }, + { + "part_num": "15068pr0046", + "name": "Slope Curved 2 x 2 x 2/3 with 'turbo' print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0046/slope-curved-2-x-2-x-23-with-turbo-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6342999.jpg", + "external_ids": { + "BrickLink": [ + "15068pb333" + ], + "BrickOwl": [ + "825589" + ], + "Brickset": [ + "78284" + ], + "LDraw": [ + "15068pt2" + ], + "LEGO": [ + "78284" + ] + } + }, + { + "part_num": "15068pr0048", + "name": "Slope Curved 2 x 2 x 2/3 with Dark Green Scale sprint", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0048/slope-curved-2-x-2-x-23-with-dark-green-scale-sprint/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6345730.jpg", + "external_ids": { + "BrickLink": [ + "15068pb367" + ], + "BrickOwl": [ + "590708" + ], + "Brickset": [ + "78564" + ], + "LEGO": [ + "78564" + ] + } + }, + { + "part_num": "15068pr0049", + "name": "Slope Curved 2 x 2 x 2/3 with Vet Logo with Paw Print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0049/slope-curved-2-x-2-x-23-with-vet-logo-with-paw-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6343799.jpg", + "external_ids": { + "BrickLink": [ + "15068pb362" + ], + "BrickOwl": [ + "691925" + ], + "Brickset": [ + "78315" + ], + "LDraw": [ + "15068p0a" + ], + "LEGO": [ + "78315" + ] + } + }, + { + "part_num": "15068pr0050", + "name": "Slope Curved 2 x 2 x 2/3 with Black Dalmatian Spot print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0050/slope-curved-2-x-2-x-23-with-black-dalmatian-spot-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6350448.jpg", + "external_ids": { + "BrickLink": [ + "15068pb365" + ], + "BrickOwl": [ + "307223" + ], + "Brickset": [ + "79112" + ], + "LEGO": [ + "79112" + ] + } + }, + { + "part_num": "15068pr0051", + "name": "Slope Curved 2 x 2 x 2/3 with Orange Dot Print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0051/slope-curved-2-x-2-x-23-with-orange-dot-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6357108.jpg", + "external_ids": { + "BrickLink": [ + "15068pb385" + ], + "BrickOwl": [ + "13342" + ], + "Brickset": [ + "79719" + ], + "LEGO": [ + "79719" + ] + } + }, + { + "part_num": "15068pr0052", + "name": "Slope Curved 2 x 2 x 2/3 with Black Mouth, White Eyes print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0052/slope-curved-2-x-2-x-23-with-black-mouth-white-eyes-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6353855.jpg", + "external_ids": { + "BrickLink": [ + "15068pb394" + ], + "BrickOwl": [ + "1301364" + ], + "Brickset": [ + "79531" + ], + "LEGO": [ + "79531" + ] + } + }, + { + "part_num": "15068pr0053", + "name": "Slope Curved 2 x 2 x 2/3 with Animal, Teeth, Medium Azure Goggles print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0053/slope-curved-2-x-2-x-23-with-animal-teeth-medium-azure-goggles-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6353860.jpg", + "external_ids": { + "BrickLink": [ + "15068pb405" + ], + "BrickOwl": [ + "702108" + ], + "Brickset": [ + "79536" + ], + "LEGO": [ + "79536" + ] + } + }, + { + "part_num": "15068pr0054", + "name": "Slope Curved 2 x 2 x 2/3 with Fire Logo Print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0054/slope-curved-2-x-2-x-23-with-fire-logo-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6375486.jpg", + "external_ids": { + "BrickLink": [ + "15068pb395" + ], + "BrickOwl": [ + "1379327" + ], + "Brickset": [ + "84783" + ], + "LEGO": [ + "84783" + ] + } + }, + { + "part_num": "15068pr0055", + "name": "Slope Curved 2 x 2 x 2/3 with Dark Purple/Light Bluish Grey Stripes print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0055/slope-curved-2-x-2-x-23-with-dark-purplelight-bluish-grey-stripes-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6376569.jpg", + "external_ids": { + "BrickLink": [ + "15068pb412" + ], + "BrickOwl": [ + "152006" + ], + "Brickset": [ + "87235" + ], + "LEGO": [ + "87235" + ] + } + }, + { + "part_num": "15068pr0057", + "name": "Slope Curved 2 x 2 x 2/3 with Open Black Mouth, White Fangs print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0057/slope-curved-2-x-2-x-23-with-open-black-mouth-white-fangs-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6382147.jpg", + "external_ids": { + "BrickLink": [ + "15068pb407" + ], + "BrickOwl": [ + "630743" + ], + "Brickset": [ + "94803" + ], + "LEGO": [ + "94803" + ] + } + }, + { + "part_num": "15068pr0058", + "name": "Slope Curved 2 x 2 x 2/3 with Classic Space Logo Print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0058/slope-curved-2-x-2-x-23-with-classic-space-logo-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6384572.jpg", + "external_ids": { + "BrickLink": [ + "15068pb403" + ], + "BrickOwl": [ + "583054" + ], + "Brickset": [ + "98838" + ], + "LEGO": [ + "98838" + ] + } + }, + { + "part_num": "15068pr0059", + "name": "Slope Curved 2 x 2 x 2/3 with Yellow Eyes, Open Mouth print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0059/slope-curved-2-x-2-x-23-with-yellow-eyes-open-mouth-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6382049.jpg", + "external_ids": { + "BrickLink": [ + "15068pb408" + ], + "BrickOwl": [ + "736077" + ], + "Brickset": [ + "94072" + ], + "LEGO": [ + "94072" + ] + } + }, + { + "part_num": "15068pr0061", + "name": "Slope Curved 2 x 2 x 2/3 with Yellow/Lime Circle/Eye/Arc Reactor print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0061/slope-curved-2-x-2-x-23-with-yellowlime-circleeyearc-reactor-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6393405.jpg", + "external_ids": { + "BrickLink": [ + "15068pb414" + ], + "BrickOwl": [ + "692278" + ], + "Brickset": [ + "1801" + ], + "LEGO": [ + "1801" + ] + } + }, + { + "part_num": "15068pr0062", + "name": "Slope Curved 2 x 2 x 2/3 with Gold '7' in Double Circle print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0062/slope-curved-2-x-2-x-23-with-gold-7-in-double-circle-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6406665.jpg", + "external_ids": { + "BrickLink": [ + "15068pb425" + ], + "BrickOwl": [ + "184881" + ], + "Brickset": [ + "100578" + ], + "LEGO": [ + "100578" + ] + } + }, + { + "part_num": "15068pr0064", + "name": "Slope Curved 2 x 2 x 2/3 with Round White Spider Head with Pincers Print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0064/slope-curved-2-x-2-x-23-with-round-white-spider-head-with-pincers-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6404127.jpg", + "external_ids": { + "BrickLink": [ + "15068pb440" + ], + "BrickOwl": [ + "940294" + ], + "Brickset": [ + "100369" + ], + "LEGO": [ + "100369" + ] + } + }, + { + "part_num": "15068pr0065", + "name": "Slope Curved 2 x 2 x 2/3 with Blue Shape, Ribbits, Black Air Intakes print", + "part_cat_id": 37, + "part_url": "https://rebrickable.com/parts/15068pr0065/slope-curved-2-x-2-x-23-with-blue-shape-ribbits-black-air-intakes-print/", + "part_img_url": "https://cdn.rebrickable.com/media/parts/elements/6408454.jpg", + "external_ids": { + "BrickLink": [ + "15068pb464" + ], + "BrickOwl": [ + "1071871" + ], + "Brickset": [ + "100679" + ], + "LEGO": [ + "100679" + ] + } + } + ] + } + } +} + + + + diff --git a/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_parts_search_bad_key.json b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_parts_search_bad_key.json new file mode 100644 index 0000000000..73c30c9899 --- /dev/null +++ b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_parts_search_bad_key.json @@ -0,0 +1,36 @@ +{ + "request": { + "urlPathPattern": "/rebrickable/api/v3/lego/parts/", + "method": "GET", + "headers": { + "Authorization": { + "not": { + "equalTo": "key devKey" + } + } + }, + "queryParameters": { + "search": { + "matches": ".*" + } + } + }, + "response": { + "status": 401, + "delayDistribution": { + "type": "lognormal", + "median": 100, + "sigma": 0.4 + }, + "headers": { + "Content-Type": "application/json" + }, + "jsonBody": { + "detail": "Invalid Token." + } + } +} + + + + diff --git a/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_parts_search_not_found.json b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_parts_search_not_found.json new file mode 100644 index 0000000000..594bb21102 --- /dev/null +++ b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_parts_search_not_found.json @@ -0,0 +1,39 @@ +{ + "request": { + "urlPathPattern": "/rebrickable/api/v3/lego/parts/", + "method": "GET", + "headers": { + "Authorization": { + "equalTo": "key devKey" + } + }, + "queryParameters": { + "search": { + "not": { + "equalTo": "2x2 brick" + } + } + } + }, + "response": { + "status": 200, + "delayDistribution": { + "type": "lognormal", + "median": 500, + "sigma": 0.4 + }, + "headers": { + "Content-Type": "application/json" + }, + "jsonBody": { + "count": 0, + "next": null, + "previous": null, + "results": [] + } + } +} + + + + diff --git a/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_set_by_num.json b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_set_by_num.json new file mode 100644 index 0000000000..127f318086 --- /dev/null +++ b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_set_by_num.json @@ -0,0 +1,36 @@ +{ + "request": { + "urlPathPattern": "/rebrickable/api/v3/lego/sets/21309-1/", + "method": "GET", + "headers": { + "Authorization": { + "equalTo": "key devKey" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "delayDistribution": { + "type": "lognormal", + "median": 5000, + "sigma": 0.4 + }, + "jsonBody": { + "set_num": "21309-1", + "name": "NASA Apollo Saturn V", + "year": 2017, + "theme_id": 576, + "num_parts": 1969, + "set_img_url": "https://cdn.rebrickable.com/media/sets/21309-1/9948.jpg", + "set_url": "https://rebrickable.com/sets/21309-1/nasa-apollo-saturn-v/", + "last_modified_dt": "2020-11-03T19:18:05.622167Z" + } + } +} + + + + diff --git a/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_set_by_num_bad_key.json b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_set_by_num_bad_key.json new file mode 100644 index 0000000000..4c0af8027a --- /dev/null +++ b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_set_by_num_bad_key.json @@ -0,0 +1,31 @@ +{ + "request": { + "urlPathPattern": "/rebrickable/api/v3/lego/sets/(.*)/", + "method": "GET", + "headers": { + "Authorization": { + "not": { + "equalTo": "key devKey" + } + } + } + }, + "response": { + "status": 401, + "delayDistribution": { + "type": "lognormal", + "median": 100, + "sigma": 0.4 + }, + "headers": { + "Content-Type": "application/json" + }, + "jsonBody": { + "detail": "Invalid Token." + } + } +} + + + + diff --git a/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_set_by_num_not_found.json b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_set_by_num_not_found.json new file mode 100644 index 0000000000..0b1cbf7e59 --- /dev/null +++ b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_set_by_num_not_found.json @@ -0,0 +1,29 @@ +{ + "request": { + "urlPathPattern": "/rebrickable/api/v3/lego/sets/(?!21309-1(?=/|$))([^/]+)/$", + "method": "GET", + "headers": { + "Authorization": { + "equalTo": "key devKey" + } + } + }, + "response": { + "status": 404, + "delayDistribution": { + "type": "lognormal", + "median": 500, + "sigma": 0.4 + }, + "headers": { + "Content-Type": "application/json" + }, + "jsonBody": { + "detail": "No Set matches the given query." + } + } +} + + + + diff --git a/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_sets_search.json b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_sets_search.json new file mode 100644 index 0000000000..f335c0e698 --- /dev/null +++ b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_sets_search.json @@ -0,0 +1,68 @@ +{ + "request": { + "urlPathPattern": "/rebrickable/api/v3/lego/sets/", + "method": "GET", + "headers": { + "Authorization": { + "equalTo": "key devKey" + } + }, + "queryParameters": { + "search": { + "equalTo": "Saturn V" + } + } + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "delayDistribution": { + "type": "lognormal", + "median": 7000, + "sigma": 0.4 + }, + "jsonBody": { + "count": 3, + "next": null, + "previous": null, + "results": [ + { + "set_num": "21309-1", + "name": "NASA Apollo Saturn V", + "year": 2017, + "theme_id": 576, + "num_parts": 1969, + "set_img_url": "https://cdn.rebrickable.com/media/sets/21309-1/9948.jpg", + "set_url": "https://rebrickable.com/sets/21309-1/nasa-apollo-saturn-v/", + "last_modified_dt": "2020-11-03T19:18:05.622167Z" + }, + { + "set_num": "7468-1", + "name": "Saturn V Moon Mission", + "year": 2003, + "theme_id": 387, + "num_parts": 179, + "set_img_url": "https://cdn.rebrickable.com/media/sets/7468-1/15677.jpg", + "set_url": "https://rebrickable.com/sets/7468-1/saturn-v-moon-mission/", + "last_modified_dt": "2016-04-23T11:48:38.315232Z" + }, + { + "set_num": "92176-1", + "name": "NASA Apollo Saturn V", + "year": 2020, + "theme_id": 576, + "num_parts": 1969, + "set_img_url": "https://cdn.rebrickable.com/media/sets/92176-1/75029.jpg", + "set_url": "https://rebrickable.com/sets/92176-1/nasa-apollo-saturn-v/", + "last_modified_dt": "2021-01-04T10:03:48.361554Z" + } + ] + } + } +} + + + + diff --git a/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_sets_search_bad_key.json b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_sets_search_bad_key.json new file mode 100644 index 0000000000..2c263740d1 --- /dev/null +++ b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_sets_search_bad_key.json @@ -0,0 +1,36 @@ +{ + "request": { + "urlPathPattern": "/rebrickable/api/v3/lego/sets/", + "method": "GET", + "headers": { + "Authorization": { + "not": { + "equalTo": "key devKey" + } + } + }, + "queryParameters": { + "search": { + "matches": ".*" + } + } + }, + "response": { + "status": 401, + "delayDistribution": { + "type": "lognormal", + "median": 100, + "sigma": 0.4 + }, + "headers": { + "Content-Type": "application/json" + }, + "jsonBody": { + "detail": "Invalid Token." + } + } +} + + + + diff --git a/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_sets_search_not_found.json b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_sets_search_not_found.json new file mode 100644 index 0000000000..1cda75197b --- /dev/null +++ b/software/plugins/external-item-search/dev/services/mappings/rebrickable/rebrickable_sets_search_not_found.json @@ -0,0 +1,39 @@ +{ + "request": { + "urlPathPattern": "/rebrickable/api/v3/lego/sets/", + "method": "GET", + "headers": { + "Authorization": { + "equalTo": "key devKey" + } + }, + "queryParameters": { + "search": { + "not": { + "equalTo": "Saturn V" + } + } + } + }, + "response": { + "status": 200, + "delayDistribution": { + "type": "lognormal", + "median": 500, + "sigma": 0.4 + }, + "headers": { + "Content-Type": "application/json" + }, + "jsonBody": { + "count": 0, + "next": null, + "previous": null, + "results": [] + } + } +} + + + + diff --git a/software/plugins/external-item-search/dev/services/mappings/upcitemdb/upcitemdb_barcode.json b/software/plugins/external-item-search/dev/services/mappings/upcitemdb/upcitemdb_barcode.json index 0e017783ff..01c20df270 100644 --- a/software/plugins/external-item-search/dev/services/mappings/upcitemdb/upcitemdb_barcode.json +++ b/software/plugins/external-item-search/dev/services/mappings/upcitemdb/upcitemdb_barcode.json @@ -17,7 +17,3 @@ } } } - - - - diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java index d07677b343..fe6167d6df 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java @@ -81,7 +81,8 @@ public Response allProviderInfo() { @PermitAll @Produces(MediaType.APPLICATION_JSON) public Multi search(@Valid @BeanParam ExtItemSearch search) { - return this.productLookupService.search(search).filter(r -> !r.getType().equals(ResultType.NO_RESULTS)); + log.debug("Searching for: {}", search); + return this.productLookupService.search(search); } } \ No newline at end of file diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupProviderInfo.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupProviderInfo.java index 1cb7d31aa5..40eb72f910 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupProviderInfo.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupProviderInfo.java @@ -2,10 +2,12 @@ import jakarta.validation.constraints.NotNull; import lombok.*; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.ItemKind; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupMethod; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupSource; import java.net.URI; -import java.net.URL; +import java.util.Collection; import java.util.List; /** @@ -19,7 +21,7 @@ public class ExtItemLookupProviderInfo implements Comparable brands = List.of(); + public Collection getLookupMethods(){ + return this.getId().supportedMethods; + } - @NotNull - @NonNull - @lombok.Builder.Default - private List kinds = List.of(); + public Collection getLookupSources(){ + return this.getId().supportedSources; + } private boolean enabled; diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemSearch.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemSearch.java index 69ee282a4d..afef29c591 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemSearch.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemSearch.java @@ -1,8 +1,10 @@ package tech.ebp.oqm.plugin.extItemSearch.model; +import io.smallrye.config.WithDefault; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import jakarta.ws.rs.DefaultValue; import jakarta.ws.rs.QueryParam; import lombok.AllArgsConstructor; import lombok.Builder; @@ -12,8 +14,9 @@ import lombok.Setter; import lombok.ToString; import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.ItemKind; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupType; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupMethod; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupSource; import java.util.List; @@ -25,26 +28,29 @@ @NoArgsConstructor public class ExtItemSearch { - @Parameter(description = "The kind of item to search for. If empty, will search for any item. If multiple, treated as an 'or'.") - @QueryParam("kind") - List itemKinds; + @Parameter(description = "The type of lookup to perform. If empty, will perform a text search.") + @QueryParam("lookupMethod") + @DefaultValue("TEXT") + List lookupMethods; - @Parameter(description = "The brand of item to search for. If empty, will search for any item. If multiple, treated as an 'or'.") - @QueryParam("brand") - List itemBrands; - - @Parameter(description = "The type of lookup to perform. If empty, will perform a free text search.") - @QueryParam("lookupType") - List lookupTypes; - - @Parameter(description = "The service(s) to use to search. If empty, will search all available services.") + @Parameter(description = "The data source(s) to use to search. If empty, any are used.") @QueryParam("service") - List services; + List services; + @Parameter( + description = "The source(s) to use to search. Distinct from 'services', as some sources can pull from many services. Example, one service might present data from Amazon," + + " and other retailers. If empty, will search all available." + ) + @QueryParam("source") + List sources; @NonNull @NotNull @NotBlank @QueryParam("q") String search; + + @QueryParam("keepNotFound") + @DefaultValue("true") + boolean keepNotFound; } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/LookupResult.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/LookupResult.java index 75fc4ea2ca..b048aac176 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/LookupResult.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/LookupResult.java @@ -9,7 +9,9 @@ import lombok.NoArgsConstructor; import lombok.NonNull; import lombok.experimental.SuperBuilder; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupType; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupMethod; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupSource; @Data @AllArgsConstructor @@ -30,10 +32,13 @@ public abstract class LookupResult { @NonNull @NotNull - @NotBlank - private String source; + private LookupService service; @NonNull @NotNull - private LookupType lookupType; + private LookupSource source; + + @NonNull + @NotNull + private LookupMethod method; } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/ExtItemLookupService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/ExtItemLookupService.java index df1647f34a..28a7d23684 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/ExtItemLookupService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/ExtItemLookupService.java @@ -1,6 +1,5 @@ package tech.ebp.oqm.plugin.extItemSearch.service; -import io.opentelemetry.instrumentation.annotations.WithSpan; import io.smallrye.mutiny.Multi; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; @@ -8,17 +7,15 @@ import lombok.extern.slf4j.Slf4j; import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemSearch; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ResultType; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.barcodeLookup.BarcodeLookupService; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.dataKick.DatakickService; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.rebrickable.RebrickableService; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.upcItemDb.UpcItemDbService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.ItemKind; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupType; import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; import java.util.*; -import java.util.stream.Stream; @ApplicationScoped @Slf4j @@ -54,68 +51,31 @@ public List getProductProviderInfo() { return servicesToInfoList(this.searchServices); } - public List searchServicesMatching(ExtItemSearch search) { - Stream pending = this.searchServices.stream(); - - if (search.getServices() != null && !search.getServices().isEmpty()) { - pending = pending.filter(curService-> - search.getServices().contains(curService.getProviderInfo().getId()) - ); - } - if (search.getItemKinds() != null && !search.getItemKinds().isEmpty()) { - pending = pending.filter(curService->{ - Collection supportedKinds = curService.getProviderInfo().getKinds(); - - if (supportedKinds.isEmpty()) { - return true; - } - - return search.getItemKinds().stream().anyMatch(supportedKinds::contains); - } - ); - } - if (search.getItemBrands() != null && !search.getItemBrands().isEmpty()) { - pending = pending.filter(curService->{ - Collection supportedBrands = curService.getProviderInfo().getBrands(); - - if (supportedBrands.isEmpty()) { - return true; - } - - return search.getItemBrands().stream().anyMatch(supportedBrands::contains); - } - ); - } - - return pending.toList(); - } public Multi search(ExtItemSearch search) { + List> resultUnis = new ArrayList<>(this.searchServices.size()); - List>> resultUnis = new ArrayList<>(this.searchServices.size()); - - for (ItemSearchService curService : this.searchServicesMatching(search)) { - if (search.getLookupTypes() == null || search.getLookupTypes().isEmpty()) { - resultUnis.add(curService.search(LookupType.FREE_TEXT, search.getSearch())); - continue; - } - - for (LookupType curType : search.getLookupTypes()) { + for (ItemSearchService curService : this.searchServices) { + if(search.getServices().isEmpty() || search.getServices().contains(curService.getProviderInfo().getId())) resultUnis.add( curService.search( - curType, + search.getSources(), + search.getLookupMethods(), search.getSearch() ) ); - } } - return Multi.createBy().merging().streams( + Multi output = Multi.createBy().merging().streams( resultUnis.stream() - .filter(Optional::isPresent) - .map(Optional::get) .toList() ); + + if(!search.isKeepNotFound()){ + output = output.filter(r -> !r.getType().equals(ResultType.NO_RESULTS)); + } + + return output; } } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/ItemSearchService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/ItemSearchService.java index f07ac489a8..66fde2ca71 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/ItemSearchService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/ItemSearchService.java @@ -3,12 +3,16 @@ import io.smallrye.mutiny.Multi; import jakarta.ws.rs.WebApplicationException; +import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.jboss.resteasy.reactive.ClientWebApplicationException; import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupErrResult; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupType; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupMethod; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupSource; import java.util.ArrayList; import java.util.Collection; @@ -16,82 +20,98 @@ import java.util.Optional; @Slf4j +@NoArgsConstructor public abstract class ItemSearchService { - public abstract ExtItemLookupProviderInfo getProviderInfo(); + @Getter + private LookupService service; + @Getter + private ExtItemLookupProviderInfo providerInfo; - public abstract boolean isEnabled(); + private boolean enabled = false; - public Optional> searchName(String search) { - return Optional.empty(); + public boolean isEnabled(){ + return this.enabled; } - public Optional> searchUrl(String search) { - return Optional.empty(); + protected ItemSearchService( + boolean enabled, + LookupService id, + ExtItemLookupProviderInfo.Builder providerInfo + ) { + this.enabled = enabled; + this.service = id; + this.providerInfo = providerInfo + .id(this.getService()) + .enabled(this.isEnabled()) + .build(); } - public Optional> searchBarcode(String search) { - return Optional.empty(); - } + protected abstract Multi performSearch(LookupSource source, LookupMethod lookupMethod, String term); - public Optional> searchPartNum(String search) { - return Optional.empty(); + public final Multi search( + LookupMethod lookupMethod, + LookupSource source, + String term + ) { + if (!this.getService().supportedMethods.contains(lookupMethod)) { + return Multi.createFrom().empty(); + } + if (!this.getService().supportedSources.contains(source)) { + return Multi.createFrom().empty(); + } + + return this.performSearch(source, lookupMethod, term); } - public Multi freeSearch(String search) { - List>> resultUniOps = new ArrayList<>(LookupType.values().length); + public final Multi search( + List source, + List lookupMethod, + String term + ) { + if(!this.isEnabled()){ + return Multi.createFrom().empty(); + } - for (LookupType type : LookupType.values()) { - switch (type) { - case NAME -> resultUniOps.add(this.searchName(search)); - case BARCODE -> resultUniOps.add(this.searchBarcode(search)); - case PART_NUM -> resultUniOps.add(this.searchPartNum(search)); - case URL -> resultUniOps.add(this.searchUrl(search)); + Collection methods = lookupMethod.isEmpty() ? lookupMethod : + lookupMethod.stream().filter(this.getService().supportedMethods::contains).toList(); + Collection sources = source.isEmpty() ? this.getService().supportedSources : source; + + Collection> results = new ArrayList<>(); + + for(LookupMethod curMethod : methods) { + for(LookupSource curSource : sources) { + results.add(this.search(curMethod, curSource, term)); } } - List> resultUnis = resultUniOps.stream() - .filter(Optional::isPresent) - .map(Optional::get) - .toList(); - return Multi.createBy().merging().streams(resultUnis); + return Multi.createBy().merging().streams(results); } - public Optional> search(LookupType type, String search) { - return switch (type) { - case FREE_TEXT -> Optional.of(this.freeSearch(search)); - case URL -> this.searchUrl(search); - case NAME -> this.searchName(search); - case BARCODE -> this.searchBarcode(search); - case PART_NUM -> this.searchPartNum(search); - }; - } protected Optional handleClientError( - LookupType type, + LookupSource source, + LookupMethod type, ClientWebApplicationException e ) { //noting to do by default. return Optional.empty(); } - protected LookupResult handleError(LookupType type, Throwable error) { + protected LookupResult handleError(LookupSource source, LookupMethod method, Throwable error) { log.warn("Error searching for ext items: {}", error.getMessage(), error); - ExtItemLookupErrResult.Builder builder = this.setupResponseBuilder(ExtItemLookupErrResult.builder(), type); - - builder.lookupType(type); - builder.source(this.getProviderInfo().getId()); + ExtItemLookupErrResult.Builder builder = this.setupResponseBuilder(ExtItemLookupErrResult.builder(), source, method); builder.errMessage(error.getMessage()); - if(error instanceof WebApplicationException) { + if (error instanceof WebApplicationException) { builder.errCode(((WebApplicationException) error).getResponse().getStatus()); builder.errMessage(((WebApplicationException) error).getResponse().getStatusInfo().getReasonPhrase()); - if(error instanceof ClientWebApplicationException){ - Optional handled = this.handleClientError(type, (ClientWebApplicationException) error); - if(handled.isPresent()){ + if (error instanceof ClientWebApplicationException) { + Optional handled = this.handleClientError(source, method, (ClientWebApplicationException) error); + if (handled.isPresent()) { return handled.get(); } } @@ -100,13 +120,14 @@ protected LookupResult handleError(LookupType type, Throwable error) { return builder.build(); } - protected Collection handleErrorRetCollection(LookupType type, Throwable error) { - return List.of(this.handleError(type, error)); + protected Collection handleErrorRetCollection(LookupSource source, LookupMethod method, Throwable error) { + return List.of(this.handleError(source, method, error)); } - protected > T setupResponseBuilder(T builder, LookupType type) { - builder.lookupType(type); - builder.source(this.getProviderInfo().getId()); + protected > T setupResponseBuilder(T builder, LookupSource source, LookupMethod method) { + builder.service(this.getService()); + builder.method(method); + builder.source(source); return builder; } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupClient.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupClient.java index 4e458ce3a5..fa0ca8d5f0 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupClient.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupClient.java @@ -17,4 +17,9 @@ public interface BarcodeLookupClient { @GET @CacheResult(cacheName = "barcodelookup-barcode-search") Uni searchBarcode(@QueryParam("key") String apiKey, @QueryParam("barcode") String barcode); + + @WithSpan + @GET + @CacheResult(cacheName = "barcodelookup-query-search") + Uni searchQuery(@QueryParam("key") String apiKey, @QueryParam("search") String barcode); } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupService.java index ac58042794..16160423d5 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupService.java @@ -3,15 +3,11 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; -import io.opentelemetry.instrumentation.annotations.WithSpan; import io.smallrye.mutiny.Multi; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.NonNull; import lombok.extern.slf4j.Slf4j; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.rest.client.inject.RestClient; @@ -21,12 +17,13 @@ import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResultNoResults; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupType; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupMethod; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupSource; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.ResultMappingUtils; import java.net.URI; import java.util.*; -import java.util.concurrent.CompletionStage; /** * @@ -52,31 +49,29 @@ public BarcodeLookupService( String apiKey ) { this.barcodeLookupClient = barcodeLookupClient; - - ExtItemLookupProviderInfo.Builder infoBuilder = ExtItemLookupProviderInfo - .builder() - .id("barcodelookup-com") - .displayName("BarcodeLookup.com") - .description("Comprehensive database of products, but a paid service. Can get a 2-week trial API key.") - .acceptsContributions(true) - .homepage(URI.create("https://www.barcodelookup.com/")) - .cost("Paid"); - if (apiKey == null || apiKey.isBlank()) { log.warn("API key for BarcodeLookup was null or blank."); - infoBuilder.enabled(false); this.apiKey = null; } else { - infoBuilder.enabled(enabled); this.apiKey = apiKey; } - this.providerInfo = infoBuilder.build(); + super( + enabled, + LookupService.BARCODE_LOOKUP, + ExtItemLookupProviderInfo + .builder() + .displayName("BarcodeLookup.com") + .description("Comprehensive database of products, but a paid service. Can get a 2-week trial API key.") + .acceptsContributions(true) + .homepage(URI.create("https://www.barcodelookup.com/")) + .cost("Paid") + ); } @Override public boolean isEnabled() { - return this.getProviderInfo().isEnabled(); + return super.isEnabled() && this.apiKey != null && !this.apiKey.isBlank(); } /** @@ -86,7 +81,7 @@ public boolean isEnabled() { * * @return */ - public Collection jsonNodeToSearchResults(LookupType type, JsonNode results) { + public Collection jsonNodeToSearchResults(LookupSource source, LookupMethod method, JsonNode results) { log.debug("Data from BarcodeLookup: {}", results.toPrettyString()); ArrayNode resultsAsArr = (ArrayNode) results.get("products"); @@ -94,7 +89,7 @@ public Collection jsonNodeToSearchResults(LookupType type, JsonNod for (JsonNode result : resultsAsArr) { ObjectNode curResultJson = (ObjectNode) result; - ExtItemLookupResult.Builder resultBuilder = this.setupResponseBuilder(ExtItemLookupResult.builder(), type); + ExtItemLookupResult.Builder resultBuilder = this.setupResponseBuilder(ExtItemLookupResult.builder(), source, method); Map attributes = new HashMap<>(); @@ -164,23 +159,35 @@ public Collection jsonNodeToSearchResults(LookupType type, JsonNod } @Override - public Optional> searchBarcode(String search) { - return Optional.of( - this.barcodeLookupClient.searchBarcode(this.apiKey, search) - .map(results->this.jsonNodeToSearchResults(LookupType.BARCODE, results)) - .onFailure().recoverWithItem(e->this.handleErrorRetCollection(LookupType.BARCODE, e)) - - .onItem().transformToMulti(collection-> - Multi.createFrom().iterable(collection) - ) - ); + protected Multi performSearch(LookupSource source, LookupMethod method, String term) { + return switch (source) { + case BARCODE_LOOKUP -> + switch (method) { + case BARCODE -> this.barcodeLookupClient.searchBarcode(this.apiKey, term) + .map(results->this.jsonNodeToSearchResults(source, method, results)) + .onFailure().recoverWithItem(e->this.handleErrorRetCollection(source, method, e)) + + .onItem().transformToMulti(collection-> + Multi.createFrom().iterable(collection) + ); + case TEXT -> this.barcodeLookupClient.searchQuery(this.apiKey, term) + .map(results->this.jsonNodeToSearchResults(source, method, results)) + .onFailure().recoverWithItem(e->this.handleErrorRetCollection(source, method, e)) + + .onItem().transformToMulti(collection-> + Multi.createFrom().iterable(collection) + ); + default -> throw new IllegalArgumentException("Invalid lookup method: " + method); + }; + default -> throw new IllegalArgumentException("Invalid lookup source: " + source); + }; } @Override - protected Optional handleClientError(LookupType type, ClientWebApplicationException e) { + protected Optional handleClientError(LookupSource source, LookupMethod method, ClientWebApplicationException e) { if (e.getResponse().getStatus() == 404) { return Optional.of( - this.setupResponseBuilder(LookupResultNoResults.builder(), type) + this.setupResponseBuilder(LookupResultNoResults.builder(), source, method) .detail("No items found.") .build() ); diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java index b922d42fee..d81e0171f2 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java @@ -4,9 +4,7 @@ import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import io.opentelemetry.instrumentation.annotations.WithSpan; -import io.quarkus.cache.CacheResult; import io.smallrye.mutiny.Multi; -import io.smallrye.mutiny.Uni; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import lombok.Getter; @@ -20,9 +18,10 @@ import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResultNoResults; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupType; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupMethod; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupSource; -import java.io.InputStream; import java.net.URI; import java.util.*; @@ -48,22 +47,18 @@ public DatakickService( boolean enabled ) { this.dataKickLookupClient = dataKickLookupClient; - this.providerInfo = ExtItemLookupProviderInfo - .builder() - .id("datakick") - .displayName("Datakick") - .enabled(enabled) - .description( - "The open product database, free and open database of products. Mostly for home and food goods. Limited size of database, but free and open to contributions.") - .acceptsContributions(true) - .homepage(URI.create("https://gtinsearch.org/")) - .cost("Free") - .build(); - } - - @Override - public boolean isEnabled() { - return this.getProviderInfo().isEnabled(); + super( + enabled, + LookupService.DATAKICK, + ExtItemLookupProviderInfo + .builder() + .displayName("Datakick") + .description( + "The open product database, free and open database of products. Mostly for home and food goods. Limited size of database, but free and open to contributions.") + .acceptsContributions(true) + .homepage(URI.create("https://gtinsearch.org/")) + .cost("Free") + ); } /** @@ -74,7 +69,7 @@ public boolean isEnabled() { * @return */ @WithSpan - public Collection jsonNodeToSearchResults(LookupType type, ArrayNode results) { + public Collection jsonNodeToSearchResults(LookupSource source, LookupMethod method, ArrayNode results) { log.debug("Data from Datakick: {}", results); List resultsList = new ArrayList<>(results.size()); @@ -111,37 +106,38 @@ public Collection jsonNodeToSearchResults(LookupType type, ArrayNo } } - resultsList.add(ExtItemLookupResult - .builder() - .lookupType(type) - .source(this.getProviderInfo().getDisplayName()) - .name(name) - .unifiedName(name) - .attributes(attributes) - .build() + resultsList.add( + this.setupResponseBuilder(ExtItemLookupResult.builder(), source, method) + .name(name) + .unifiedName(name) + .attributes(attributes) + .build() ); } return resultsList; } @Override - public Optional> searchBarcode(String search) { - return Optional.of( - this.dataKickLookupClient.getFromUpcCode(search) - .map(results->this.jsonNodeToSearchResults(LookupType.BARCODE, results)) - .onFailure().recoverWithItem(e -> this.handleErrorRetCollection(LookupType.BARCODE, e)) - - .onItem().transformToMulti(collection-> - Multi.createFrom().iterable(collection) - ) - ); + protected Multi performSearch(LookupSource source, LookupMethod method, String term) { + return switch (source) { + case DATAKICK -> + switch (method) { + case BARCODE -> this.dataKickLookupClient.getFromUpcCode(term) + .map(result->this.jsonNodeToSearchResults(source, method, result)) + .onFailure().recoverWithItem(e->this.handleErrorRetCollection(source, method, e)) + .onItem().transformToMulti(collection-> Multi.createFrom().iterable(collection)); + default -> throw new IllegalArgumentException("Invalid lookup method: " + method); + }; + default -> throw new IllegalArgumentException("Invalid lookup source: " + source); + }; } + @Override - protected Optional handleClientError(LookupType type, ClientWebApplicationException e) { + protected Optional handleClientError(LookupSource source, LookupMethod method, ClientWebApplicationException e) { if (e.getResponse().getStatus() == 404) { return Optional.of( - this.setupResponseBuilder(LookupResultNoResults.builder(), type) + this.setupResponseBuilder(LookupResultNoResults.builder(), source, method) .detail("No items found.") .build() ); diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableLookupClient.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableLookupClient.java index d6aceed1d3..a84aff6542 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableLookupClient.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableLookupClient.java @@ -1,6 +1,5 @@ package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.rebrickable; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import io.opentelemetry.instrumentation.annotations.WithSpan; import io.quarkus.cache.CacheResult; @@ -12,8 +11,6 @@ import jakarta.ws.rs.QueryParam; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; -import java.util.concurrent.CompletionStage; - @Path("/api/v3/lego/") @RegisterRestClient(configKey = "rebrickable") public interface RebrickableLookupClient { @@ -21,6 +18,24 @@ public interface RebrickableLookupClient { @GET @Path("parts/{partNo}/") @WithSpan - @CacheResult(cacheName = "rebrickable-part-num-search") - Uni getFromPartNum(@HeaderParam("Authorization") String apiKey, @PathParam("partNo") String partNumber); + @CacheResult(cacheName = "rebrickable-part-num-get") + Uni partFromNum(@HeaderParam("Authorization") String apiKey, @PathParam("partNo") String partNumber); + + @GET + @Path("parts/") + @WithSpan + @CacheResult(cacheName = "rebrickable-part-search") + Uni partsSearch(@HeaderParam("Authorization") String apiKey, @QueryParam("search") String query); + + @GET + @Path("sets/{setNo}/") + @WithSpan + @CacheResult(cacheName = "rebrickable-set-num-get") + Uni setFromNum(@HeaderParam("Authorization") String apiKey, @PathParam("setNo") String setNumber); + + @GET + @Path("sets/") + @WithSpan + @CacheResult(cacheName = "rebrickable-set-search") + Uni setsSearch(@HeaderParam("Authorization") String apiKey, @QueryParam("search") String query); } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java index 58458dac81..d795706ace 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java @@ -5,12 +5,9 @@ import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import io.opentelemetry.instrumentation.annotations.WithSpan; -import io.quarkus.cache.CacheResult; import io.smallrye.mutiny.Multi; -import io.smallrye.mutiny.Uni; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import lombok.Getter; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.eclipse.microprofile.config.inject.ConfigProperty; @@ -21,13 +18,15 @@ import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResultNoResults; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.ItemKind; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupType; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupMethod; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupSource; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.ResultMappingUtils; import java.io.InputStream; import java.net.URI; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -45,8 +44,6 @@ public class RebrickableService extends ItemSearchService { private static final String BRAND = "LEGO"; - @Getter - ExtItemLookupProviderInfo providerInfo; RebrickableLookupClient rebrickableLookupClient; private String apiKey; private ObjectMapper objectMapper; @@ -64,41 +61,33 @@ public RebrickableService( this.rebrickableLookupClient = rebrickableLookupClient; this.apiKey = apiKey; this.objectMapper = objectMapper; - - ExtItemLookupProviderInfo.Builder infoBuilder = ExtItemLookupProviderInfo - .builder() - .id("rebrickable") - .displayName("Rebrickable") - .description("A database of LEGO(TM) pieces. Free, but requires you to get your own key.") - .acceptsContributions(false) - .cost("Free") - .brands(List.of(BRAND)) - .kinds(List.of(ItemKind.LEGO)) - .homepage(URI.create("https://rebrickable.com")); - if (apiKey == null || apiKey.isBlank()) { log.warn("API key for Rebrickable was null or blank."); - infoBuilder.enabled(false); this.apiKey = null; } else { - infoBuilder.enabled(enabled); this.apiKey = apiKey; } - - this.providerInfo = infoBuilder.build(); + super( + enabled, + LookupService.REBRICKABLE, + ExtItemLookupProviderInfo + .builder() + .displayName("Rebrickable") + .description("A database of LEGO(TM) pieces. Free, but requires you to get your own key.") + .acceptsContributions(false) + .cost("Free") + .homepage(URI.create("https://rebrickable.com")) + ); } @Override public boolean isEnabled() { - return this.providerInfo.isEnabled() && this.apiKey != null && !this.apiKey.isBlank(); + return super.isEnabled() && this.apiKey != null && !this.apiKey.isBlank(); } - @WithSpan - public LookupResult jsonNodeToSearchResults(LookupType type, ObjectNode results) { + public LookupResult partJsonToResult(LookupSource source, LookupMethod method, ObjectNode results) { log.info("Search result: {}", results); - ExtItemLookupResult.Builder resultBuilder = ExtItemLookupResult.builder() - .lookupType(type) - .source(this.getProviderInfo().getDisplayName()); + ExtItemLookupResult.Builder resultBuilder = this.setupResponseBuilder(ExtItemLookupResult.builder(), source, method); List images = new ArrayList<>(); Map links = new HashMap<>(); @@ -161,29 +150,135 @@ public LookupResult jsonNodeToSearchResults(LookupType type, ObjectNode results) return resultBuilder.build(); } + public Collection partSearchJsonToResults(LookupSource source, LookupMethod method, ObjectNode results) { + long count = results.get("count").asLong(); + + if(count == 0){ + return List.of(this.setupResponseBuilder(LookupResultNoResults.builder(), source, method) + .detail("No results found.") + .build()); + } + + ArrayNode resultsAsArr = (ArrayNode) results.get("results"); + List resultList = new ArrayList<>(resultsAsArr.size()); + + for (JsonNode result : resultsAsArr) { + resultList.add(this.partJsonToResult(source, method, (ObjectNode) result)); + } + + return resultList; + } + + public LookupResult setJsonToResult(LookupSource source, LookupMethod method, ObjectNode results) { + log.info("Search result: {}", results); + ExtItemLookupResult.Builder resultBuilder = this.setupResponseBuilder(ExtItemLookupResult.builder(), source, method); + + List images = new ArrayList<>(); + Map links = new HashMap<>(); + Map identifiers = new HashMap<>(); + Map attributes = new HashMap<>(); + + attributes.put("brand", BRAND); + + for (Map.Entry curField : results.properties()) { + String curFieldName = curField.getKey(); + JsonNode curFieldVal = curField.getValue(); + + if(ResultMappingUtils.isFieldEmpty(curFieldVal)){ + continue; + } + + switch (curFieldName) { + case "name": + resultBuilder.name(curFieldVal.asText()); + resultBuilder.unifiedName(curFieldVal.asText()); + break; + case "set_num": + identifiers.put("legoSetNum", curFieldVal.asText()); + break; + case "set_img_url": + images.add(curFieldVal.asText()); + break; + case "set_url": + links.put("rebrickable", curFieldVal.asText()); + break; + default: + if (curFieldVal.isValueNode()) { + attributes.put(curFieldName, curFieldVal.asText()); + } + } + } + + resultBuilder.identifiers(identifiers); + resultBuilder.images(images); + resultBuilder.links(links); + resultBuilder.attributes(attributes); + + return resultBuilder.build(); + } + + public Collection setSearchJsonToResults(LookupSource source, LookupMethod method, ObjectNode results) { + long count = results.get("count").asLong(); + + if(count == 0){ + return List.of(this.setupResponseBuilder(LookupResultNoResults.builder(), source, method) + .detail("No results found.") + .build()); + } + + ArrayNode resultsAsArr = (ArrayNode) results.get("results"); + List resultList = new ArrayList<>(resultsAsArr.size()); + + for (JsonNode result : resultsAsArr) { + resultList.add(this.setJsonToResult(source, method, (ObjectNode) result)); + } + + return resultList; + } + protected String getApiKey() { return "key " + this.apiKey; } @Override - public Optional> searchPartNum(String partNum) { - return Optional.of( - this.rebrickableLookupClient.getFromPartNum(this.getApiKey(), partNum) - .map(result->this.jsonNodeToSearchResults(LookupType.PART_NUM, result)) - .onFailure().recoverWithItem(e->this.handleError(LookupType.PART_NUM, e)) - .toMulti() - ); + protected Multi performSearch(LookupSource source, LookupMethod method, String term) { + return switch (source) { + case REBRICKABLE -> + switch (method) { + case PART_NUM -> this.rebrickableLookupClient.partFromNum(this.getApiKey(), term) + .map(result->this.partJsonToResult(source, method, result)) + .onFailure().recoverWithItem(e->this.handleError(source, method, e)) + .toMulti(); + + case SET_NUM -> this.rebrickableLookupClient.setFromNum(this.getApiKey(), term) + .map(result->this.partJsonToResult(source, method, result)) + .onFailure().recoverWithItem(e->this.handleError(source, method, e)) + .toMulti(); + case TEXT -> Multi.createBy().merging().streams( + this.rebrickableLookupClient.partsSearch(this.getApiKey(), term) + .map(result->this.partSearchJsonToResults(source, method, result)) + .onFailure().recoverWithItem(e->this.handleErrorRetCollection(source, method, e)) + .onItem().transformToMulti(collection-> Multi.createFrom().iterable(collection)), + this.rebrickableLookupClient.setsSearch(this.getApiKey(), term) + .map(result->this.setSearchJsonToResults(source, method, result)) + .onFailure().recoverWithItem(e->this.handleErrorRetCollection(source, method, e)) + .onItem().transformToMulti(collection-> Multi.createFrom().iterable(collection)) + ); + default -> throw new IllegalArgumentException("Invalid lookup method: " + method); + }; + default -> throw new IllegalArgumentException("Invalid lookup source: " + source); + }; } @Override - protected Optional handleClientError(LookupType type, ClientWebApplicationException e) { + protected Optional handleClientError(LookupSource source, LookupMethod method, ClientWebApplicationException e) { if (e.getResponse().getStatus() == 404) { try { ObjectNode errorDeets = (ObjectNode) this.objectMapper.readTree((InputStream) e.getResponse().getEntity()); if (errorDeets.get("detail").asText().equals("No Part matches the given query.")) { return Optional.of( - this.setupResponseBuilder(LookupResultNoResults.builder(), type) + this.setupResponseBuilder(LookupResultNoResults.builder(), source, method) .detail("No Part matches the given query.") .build() ); diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/upcItemDb/UpcItemDbService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/upcItemDb/UpcItemDbService.java index 49fc52dd05..745ab21c6d 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/upcItemDb/UpcItemDbService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/upcItemDb/UpcItemDbService.java @@ -13,20 +13,25 @@ import lombok.extern.slf4j.Slf4j; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.jboss.resteasy.reactive.ClientWebApplicationException; import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResult; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; +import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResultNoResults; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupType; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupMethod; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupSource; import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.ResultMappingUtils; import java.net.URI; import java.util.*; +import static tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupMethod.BARCODE; + /** * - * - Docs: https://www.upcitemdb.com/wp/docs/main/development/getting-started/ - * - Trial docs: https://www.upcitemdb.com/api/explorer#!/lookup/get_trial_lookup + * - Docs: https://www.upcitemdb.com/wp/docs/main/development/getting-started/ - Trial docs: https://www.upcitemdb.com/api/explorer#!/lookup/get_trial_lookup */ @ApplicationScoped @Slf4j @@ -52,29 +57,26 @@ public UpcItemDbService( ) { this.upcItemDbLookupClient = upcItemDbLookupClient; this.apiKey = apiKey; - this.providerInfo = ExtItemLookupProviderInfo - .builder() - .id("upcitemdb-com") - .displayName("upcitemdb.com") - .description("A lookup database with good number of records, and a free tier with 100 requests per day.") - .acceptsContributions(false) - .homepage(URI.create("https://www.upcitemdb.com/")) - .cost("Paid, Free tier") - .enabled(enabled) - .build(); - } - - @Override - public boolean isEnabled() { - return this.getProviderInfo().isEnabled(); + super( + enabled, + LookupService.UPC_ITEM_DB, + ExtItemLookupProviderInfo + .builder() + .displayName("upcitemdb.com") + .description("A lookup database with good number of records, and a free tier with 100 requests per day.") + .acceptsContributions(false) + .homepage(URI.create("https://www.upcitemdb.com/")) + .cost("Paid, Free tier") + .enabled(enabled) + ); } public boolean hasKey() { return this.apiKey != null && !this.apiKey.isBlank(); } - private ExtItemLookupResult jsonToResult(LookupType type, ObjectNode json) { - ExtItemLookupResult.Builder resultBuilder = this.setupResponseBuilder(ExtItemLookupResult.builder(), type); + private ExtItemLookupResult jsonToResult(LookupSource source, LookupMethod method, ObjectNode json) { + ExtItemLookupResult.Builder resultBuilder = this.setupResponseBuilder(ExtItemLookupResult.builder(), source, method); Map attributes = new HashMap<>(); Map identifiers = new HashMap<>(); @@ -108,7 +110,7 @@ private ExtItemLookupResult jsonToResult(LookupType type, ObjectNode json) { } break; default: - if(curFieldVal.isTextual()){ + if (curFieldVal.isTextual()) { attributes.put(curFieldName, curFieldVal.asText()); } break; @@ -116,7 +118,6 @@ private ExtItemLookupResult jsonToResult(LookupType type, ObjectNode json) { } return resultBuilder - .source(this.getProviderInfo().getDisplayName()) .attributes(attributes) .identifiers(identifiers) .images(images) @@ -131,31 +132,19 @@ private ExtItemLookupResult jsonToResult(LookupType type, ObjectNode json) { * @return */ @WithSpan - public Collection jsonNodeToSearchResults(LookupType type, ObjectNode results) { + public Collection jsonNodeToSearchResults(LookupSource source, LookupMethod type, ObjectNode results) { log.debug("Data from upcitemdb: {}", results.toPrettyString()); ArrayNode resultsAsArr = (ArrayNode) results.get("items"); List resultList = new ArrayList<>(resultsAsArr.size()); for (JsonNode result : resultsAsArr) { - resultList.add(this.jsonToResult(type, (ObjectNode) result)); + resultList.add(this.jsonToResult(source, type, (ObjectNode) result)); } return resultList; } - @Override - public Optional> searchBarcode(String barcode) { - return Optional.of( - this.performBarcodeSearchCall(barcode) - .map(result->this.jsonNodeToSearchResults(LookupType.BARCODE, result)) - .onFailure().recoverWithItem(e->this.handleErrorRetCollection(LookupType.BARCODE, e)) - .onItem().transformToMulti(collection-> - Multi.createFrom().iterable(collection) - ) - ); - } - protected Uni performBarcodeSearchCall(String barcode) { if (this.hasKey()) { @@ -168,4 +157,33 @@ protected Uni performBarcodeSearchCall(String barcode) { return this.upcItemDbLookupClient.getFromUpcCodeTrial(barcode); } + + @Override + protected Multi performSearch(LookupSource source, LookupMethod method, String term) { + return switch (source) { + case UPC_ITEM_DB -> + switch (method) { + case BARCODE -> this.performBarcodeSearchCall(term) + .map(result->this.jsonNodeToSearchResults(source, method, result)) + .onFailure().recoverWithItem(e->this.handleErrorRetCollection(source, method, e)) + .onItem().transformToMulti(collection-> + Multi.createFrom().iterable(collection) + ); + default -> throw new IllegalArgumentException("Invalid lookup method: " + method); + }; + default -> throw new IllegalArgumentException("Invalid lookup source: " + source); + }; + } + + @Override + protected Optional handleClientError(LookupSource source, LookupMethod method, ClientWebApplicationException e) { + if (e.getResponse().getStatus() == 404) { + return Optional.of( + this.setupResponseBuilder(LookupResultNoResults.builder(), source, method) + .detail("No results found.") + .build() + ); + } + return Optional.empty(); + } } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/ItemKind.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/ItemKind.java deleted file mode 100644 index 350a5a8e14..0000000000 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/ItemKind.java +++ /dev/null @@ -1,5 +0,0 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils; - -public enum ItemKind { - LEGO -} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupType.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupMethod.java similarity index 63% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupType.java rename to software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupMethod.java index f63c7edf8c..7df588f976 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupType.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupMethod.java @@ -1,9 +1,10 @@ package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils; -public enum LookupType { - FREE_TEXT, - NAME, +public enum LookupMethod { + TEXT, BARCODE, PART_NUM, - URL, + SET_NUM, + WEBPAGE; + } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupService.java new file mode 100644 index 0000000000..d64becf2bb --- /dev/null +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupService.java @@ -0,0 +1,48 @@ +package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils; + +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.barcodeLookup.BarcodeLookupService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.dataKick.DatakickService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.rebrickable.RebrickableService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.upcItemDb.UpcItemDbService; + +import java.util.Collection; +import java.util.List; + +public enum LookupService { + BARCODE_LOOKUP( + BarcodeLookupService.class, + List.of(LookupSource.BARCODE_LOOKUP), + List.of(LookupMethod.BARCODE, LookupMethod.TEXT) + ), + DATAKICK( + DatakickService.class, + List.of(LookupSource.DATAKICK), + List.of(LookupMethod.BARCODE) + ), + REBRICKABLE( + RebrickableService.class, + List.of(LookupSource.REBRICKABLE), + List.of(LookupMethod.PART_NUM, LookupMethod.SET_NUM, LookupMethod.TEXT) + ), + UPC_ITEM_DB( + UpcItemDbService.class, + List.of(LookupSource.UPC_ITEM_DB), + List.of(LookupMethod.BARCODE) + ), + ; + + public final Class searchClass; + public final Collection supportedSources; + public final Collection supportedMethods; + + LookupService( + Class searchClass, + Collection supportedSources, + Collection supportedMethods + ) { + this.searchClass = searchClass; + this.supportedSources = supportedSources; + this.supportedMethods = supportedMethods; + } +} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupSource.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupSource.java new file mode 100644 index 0000000000..fea3853b74 --- /dev/null +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupSource.java @@ -0,0 +1,16 @@ +package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils; + +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.barcodeLookup.BarcodeLookupService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.dataKick.DatakickService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.rebrickable.RebrickableService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.upcItemDb.UpcItemDbService; + +public enum LookupSource { + BARCODE_LOOKUP, + DATAKICK, + REBRICKABLE, + UPC_ITEM_DB, + ; + +} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupTypes.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupTypes.java deleted file mode 100644 index b4c3c0dfaa..0000000000 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupTypes.java +++ /dev/null @@ -1,7 +0,0 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils; - -public final class LookupTypes { - public static final String BARCODE = "barcode"; - public static final String LEGO_PART_NUM = "legoPartNum"; - public static final String WEBPAGE = "webpage"; -} From ea29d71c636bea35a7c3f1c5a3a4df85d966e71e Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Thu, 23 Apr 2026 23:32:23 -0400 Subject: [PATCH 13/38] Plugin - Ext Item Search - fixes and final form --- .../30-plugin-ext_item_search.json | 18 ++++++++++++++++-- .../plugin-ext_item_search-config.list | 16 ++++++++++++++++ .../interfaces/ItemLookupRestInterface.java | 7 ++++--- .../extItemSearch/lifecycle/LifecycleBean.java | 10 ++++++++++ .../model/ExtItemLookupProviderInfo.java | 10 +++++++--- .../extItemSearch/model/ExtItemSearch.java | 3 +-- .../service/ExtItemLookupService.java | 4 ++++ .../searchServices/ItemSearchService.java | 1 + .../barcodeLookup/BarcodeLookupService.java | 7 ++----- .../providers/dataKick/DatakickService.java | 8 +------- .../rebrickable/RebrickableService.java | 5 ++--- .../providers/upcItemDb/UpcItemDbService.java | 18 +++++++----------- .../searchServices/utils/LookupSource.java | 6 ------ .../src/main/resources/application.yml | 4 ++-- 14 files changed, 73 insertions(+), 44 deletions(-) diff --git a/software/plugins/external-item-search/installerSrc/30-plugin-ext_item_search.json b/software/plugins/external-item-search/installerSrc/30-plugin-ext_item_search.json index cd1116c89b..391d80c044 100644 --- a/software/plugins/external-item-search/installerSrc/30-plugin-ext_item_search.json +++ b/software/plugins/external-item-search/installerSrc/30-plugin-ext_item_search.json @@ -5,8 +5,22 @@ "internalHttpPort": 80, "internalHttpsPort": 443, "internalBaseUri": "http://#{plugin.externalItemSearch.internalHost}:#{plugin.externalItemSearch.internalHttpPort}", - "apiKeys": { - + "services": { + "barcodelookup": { + "enabled": true, + "apiKey": null + }, + "datakick": { + "enabled": true, + }, + "rebrickable": { + "enabled": true, + "apiKey": null + }, + "upcitemdb": { + "enabled": true, + "apiKey": null + } } } } diff --git a/software/plugins/external-item-search/installerSrc/plugin-ext_item_search-config.list b/software/plugins/external-item-search/installerSrc/plugin-ext_item_search-config.list index 99849036f1..661088e7a0 100644 --- a/software/plugins/external-item-search/installerSrc/plugin-ext_item_search-config.list +++ b/software/plugins/external-item-search/installerSrc/plugin-ext_item_search-config.list @@ -17,5 +17,21 @@ quarkus.http.ssl.certificate.trust-store-password={cert.selfSigned.internalKeyst #smallrye.jwt.verify.key.location={infra.keycloak.externalBaseUri}/realms/{infra.keycloak.oqmRealmName}/protocol/openid-connect/certs #smallrye.jwt.client.tls.certificate.path=/etc/oqm/serviceConfig/plugin/external-item-search/files/publicCert.pem +# # Lookup config +# + +# Barcode lookup +productLookup.providers.barcodelookup-com.enabled={plugin.externalItemSearch.services.barcodelookup.enabled} +productLookup.providers.barcodelookup-com.apiKey={plugin.externalItemSearch.services.barcodelookup.apiKey} + +# Datakick +productLookup.providers.datakick.enabled={plugin.externalItemSearch.services.datakick.enabled} + +# UPC Item DB +productLookup.providers.upcitemdb.enabled={plugin.externalItemSearch.services.upcitemdb.enabled} +productLookup.providers.upcitemdb.apiKey={plugin.externalItemSearch.services.upcitemdb.apiKey} +# Rebrickable +productLookup.providers.rebrickable.enabled={plugin.externalItemSearch.services.rebrickable.enabled} +productLookup.providers.rebrickable.apiKey={plugin.externalItemSearch.services.rebrickable.apiKey} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java index fe6167d6df..48227d777b 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java @@ -29,6 +29,7 @@ import java.net.MalformedURLException; import java.net.URL; +import java.util.List; import java.util.concurrent.ExecutionException; @Slf4j @@ -58,8 +59,8 @@ public class ItemLookupRestInterface { ) @PermitAll @Produces(MediaType.APPLICATION_JSON) - public Response allProviderInfo() { - return Response.ok(this.productLookupService.getProductProviderInfo()).build(); + public List allProviderInfo() { + return this.productLookupService.getProductProviderInfo(); } @GET @@ -81,7 +82,7 @@ public Response allProviderInfo() { @PermitAll @Produces(MediaType.APPLICATION_JSON) public Multi search(@Valid @BeanParam ExtItemSearch search) { - log.debug("Searching for: {}", search); + log.debug("Search parameters: {}", search); return this.productLookupService.search(search); } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/lifecycle/LifecycleBean.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/lifecycle/LifecycleBean.java index 4084474095..bcc9c57fcd 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/lifecycle/LifecycleBean.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/lifecycle/LifecycleBean.java @@ -3,9 +3,13 @@ import io.quarkus.runtime.ShutdownEvent; import io.quarkus.runtime.StartupEvent; import jakarta.enterprise.event.Observes; +import jakarta.inject.Inject; import jakarta.inject.Singleton; import lombok.extern.slf4j.Slf4j; import org.eclipse.microprofile.config.ConfigProvider; +import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; +import tech.ebp.oqm.plugin.extItemSearch.service.ExtItemLookupService; +import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; import java.time.Duration; import java.time.ZonedDateTime; @@ -16,6 +20,8 @@ @Slf4j public class LifecycleBean { + @Inject + ExtItemLookupService extItemLookupService; private ZonedDateTime startDateTime; public static void logConfig(){ @@ -64,6 +70,10 @@ void onStart( StartupEvent ev ) { this.startLogAnnounce(); + + for(ItemSearchService curService : this.extItemLookupService.getSearchServices()) { + log.info("Loaded service: {}", curService.getProviderInfo()); + } } void onStop( diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupProviderInfo.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupProviderInfo.java index 40eb72f910..f3d4db9d5b 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupProviderInfo.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupProviderInfo.java @@ -17,6 +17,7 @@ @AllArgsConstructor @NoArgsConstructor @Builder +@ToString public class ExtItemLookupProviderInfo implements Comparable { @NotNull @@ -35,7 +36,7 @@ public class ExtItemLookupProviderInfo implements Comparable getLookupMethods(){ + public Collection getLookupMethods() { return this.getId().supportedMethods; } - public Collection getLookupSources(){ + public Collection getLookupSources() { return this.getId().supportedSources; } @@ -64,6 +65,9 @@ public static class Comparator implements java.util.Comparator getSearchServices() { + return this.searchServices; + } + public List getProductProviderInfo() { return servicesToInfoList(this.searchServices); } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/ItemSearchService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/ItemSearchService.java index 66fde2ca71..80d61a7265 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/ItemSearchService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/ItemSearchService.java @@ -25,6 +25,7 @@ public abstract class ItemSearchService { @Getter private LookupService service; + @Getter private ExtItemLookupProviderInfo providerInfo; diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupService.java index 16160423d5..0f61821d9a 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupService.java @@ -31,13 +31,10 @@ */ @ApplicationScoped @Slf4j -@NoArgsConstructor public class BarcodeLookupService extends ItemSearchService { - BarcodeLookupClient barcodeLookupClient; - @Getter - ExtItemLookupProviderInfo providerInfo; - String apiKey; + private BarcodeLookupClient barcodeLookupClient; + private String apiKey; @Inject public BarcodeLookupService( diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java index d81e0171f2..99e9a45efd 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java @@ -30,14 +30,9 @@ */ @ApplicationScoped @Slf4j -@NoArgsConstructor public class DatakickService extends ItemSearchService { - @Inject - @RestClient - DataKickLookupClient dataKickLookupClient; - @Getter - ExtItemLookupProviderInfo providerInfo; + private DataKickLookupClient dataKickLookupClient; @Inject public DatakickService( @@ -68,7 +63,6 @@ public DatakickService( * * @return */ - @WithSpan public Collection jsonNodeToSearchResults(LookupSource source, LookupMethod method, ArrayNode results) { log.debug("Data from Datakick: {}", results); diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java index d795706ace..09286c260d 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java @@ -39,12 +39,11 @@ */ @ApplicationScoped @Slf4j -@NoArgsConstructor public class RebrickableService extends ItemSearchService { private static final String BRAND = "LEGO"; - RebrickableLookupClient rebrickableLookupClient; + private RebrickableLookupClient rebrickableLookupClient; private String apiKey; private ObjectMapper objectMapper; @@ -75,8 +74,8 @@ public RebrickableService( .displayName("Rebrickable") .description("A database of LEGO(TM) pieces. Free, but requires you to get your own key.") .acceptsContributions(false) - .cost("Free") .homepage(URI.create("https://rebrickable.com")) + .cost("Free") ); } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/upcItemDb/UpcItemDbService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/upcItemDb/UpcItemDbService.java index 745ab21c6d..5be17dc475 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/upcItemDb/UpcItemDbService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/upcItemDb/UpcItemDbService.java @@ -8,8 +8,6 @@ import io.smallrye.mutiny.Uni; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import lombok.Getter; -import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.rest.client.inject.RestClient; @@ -35,15 +33,9 @@ */ @ApplicationScoped @Slf4j -@NoArgsConstructor public class UpcItemDbService extends ItemSearchService { - @Inject - @RestClient - UpcItemDbLookupClient upcItemDbLookupClient; - @Getter - ExtItemLookupProviderInfo providerInfo; - + private UpcItemDbLookupClient upcItemDbLookupClient; private String apiKey; @Inject @@ -56,7 +48,12 @@ public UpcItemDbService( String apiKey ) { this.upcItemDbLookupClient = upcItemDbLookupClient; - this.apiKey = apiKey; + if (apiKey == null || apiKey.isBlank()) { + log.warn("API key for UPCItemDb was null or blank."); + this.apiKey = null; + } else { + this.apiKey = apiKey; + } super( enabled, LookupService.UPC_ITEM_DB, @@ -67,7 +64,6 @@ public UpcItemDbService( .acceptsContributions(false) .homepage(URI.create("https://www.upcitemdb.com/")) .cost("Paid, Free tier") - .enabled(enabled) ); } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupSource.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupSource.java index fea3853b74..652ae9a500 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupSource.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupSource.java @@ -1,11 +1,5 @@ package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.barcodeLookup.BarcodeLookupService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.dataKick.DatakickService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.rebrickable.RebrickableService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.upcItemDb.UpcItemDbService; - public enum LookupSource { BARCODE_LOOKUP, DATAKICK, diff --git a/software/plugins/external-item-search/src/main/resources/application.yml b/software/plugins/external-item-search/src/main/resources/application.yml index 3ebb0e0834..43ab4279ff 100644 --- a/software/plugins/external-item-search/src/main/resources/application.yml +++ b/software/plugins/external-item-search/src/main/resources/application.yml @@ -10,10 +10,10 @@ productLookup: apiKey: " " datakick: enabled: true - upcitemdb: + rebrickable: enabled: true apiKey: " " - rebrickable: + upcitemdb: enabled: true apiKey: " " From fe5f71715d2192a30359b5f7a7945598ce6edf9b Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Thu, 23 Apr 2026 23:39:02 -0400 Subject: [PATCH 14/38] Plugin - Ext Item Search - swapping to correct metrics library and setup --- software/plugins/external-item-search/build.gradle | 4 ++-- .../installerSrc/plugin-ext_item_search-config.list | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/software/plugins/external-item-search/build.gradle b/software/plugins/external-item-search/build.gradle index 70e0f0b349..196132d9b6 100644 --- a/software/plugins/external-item-search/build.gradle +++ b/software/plugins/external-item-search/build.gradle @@ -5,7 +5,7 @@ plugins { } group 'tech.ebp.openQuarterMaster' -version '1.0.6-SNAPSHOT' +version '2.0.0-SNAPSHOT' repositories { mavenCentral() @@ -19,7 +19,7 @@ dependencies { implementation 'io.quarkus:quarkus-rest-client' implementation 'io.quarkus:quarkus-smallrye-openapi' implementation 'io.quarkus:quarkus-rest-jackson' - implementation 'io.quarkus:quarkus-opentelemetry' + implementation 'io.quarkus:quarkus-micrometer-opentelemetry' implementation 'io.quarkus:quarkus-config-yaml' implementation 'io.quarkus:quarkus-smallrye-jwt' implementation 'io.quarkus:quarkus-smallrye-health' diff --git a/software/plugins/external-item-search/installerSrc/plugin-ext_item_search-config.list b/software/plugins/external-item-search/installerSrc/plugin-ext_item_search-config.list index 661088e7a0..63c0af4ff8 100644 --- a/software/plugins/external-item-search/installerSrc/plugin-ext_item_search-config.list +++ b/software/plugins/external-item-search/installerSrc/plugin-ext_item_search-config.list @@ -17,6 +17,11 @@ quarkus.http.ssl.certificate.trust-store-password={cert.selfSigned.internalKeyst #smallrye.jwt.verify.key.location={infra.keycloak.externalBaseUri}/realms/{infra.keycloak.oqmRealmName}/protocol/openid-connect/certs #smallrye.jwt.client.tls.certificate.path=/etc/oqm/serviceConfig/plugin/external-item-search/files/publicCert.pem +{% if metrics is defined %} +# Metrics Config: +quarkus.otel.exporter.otlp.endpoint={metrics.otlp-collector.endpoint} +{% endif %} + # # Lookup config # From 5aedc8e7e360c9620c61b306363f0229e0779b41 Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Thu, 23 Apr 2026 23:45:54 -0400 Subject: [PATCH 15/38] Plugin - Ext Item Search - minor cleanup --- .../temp/api/ItemApiSearchService.java | 11 ------- .../api/product/ApiProductSearchService.java | 30 ------------------- 2 files changed, 41 deletions(-) delete mode 100644 software/plugins/external-item-search/temp/api/ItemApiSearchService.java delete mode 100644 software/plugins/external-item-search/temp/api/product/ApiProductSearchService.java diff --git a/software/plugins/external-item-search/temp/api/ItemApiSearchService.java b/software/plugins/external-item-search/temp/api/ItemApiSearchService.java deleted file mode 100644 index 020a8e1f30..0000000000 --- a/software/plugins/external-item-search/temp/api/ItemApiSearchService.java +++ /dev/null @@ -1,11 +0,0 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api; - -import com.fasterxml.jackson.databind.JsonNode; -import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResult; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; - -import java.util.List; - -public abstract class ItemApiSearchService extends ItemSearchService { - public abstract List jsonNodeToSearchResults(JsonNode results); -} diff --git a/software/plugins/external-item-search/temp/api/product/ApiProductSearchService.java b/software/plugins/external-item-search/temp/api/product/ApiProductSearchService.java deleted file mode 100644 index ef610a2835..0000000000 --- a/software/plugins/external-item-search/temp/api/product/ApiProductSearchService.java +++ /dev/null @@ -1,30 +0,0 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.product; - - -import com.fasterxml.jackson.databind.JsonNode; -import io.opentelemetry.instrumentation.annotations.WithSpan; -import lombok.extern.slf4j.Slf4j; -import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResult; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.api.ItemApiSearchService; - -import java.util.List; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; - -@Slf4j -public abstract class ApiProductSearchService extends ItemApiSearchService { - - @WithSpan - protected abstract CompletionStage performBarcodeSearchCall(String barcode); - - @WithSpan - public Optional>> searchBarcode(String barcode) { - if (!this.isEnabled()) { - return Optional.empty(); - } - CompletionStage stage = this.performBarcodeSearchCall(barcode); - - return Optional.of(stage.thenApply(this::jsonNodeToSearchResults).toCompletableFuture()); - } -} From 5e41930c377fd4724c175160a1ef1078a73af11a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Apr 2026 03:51:19 +0000 Subject: [PATCH 16/38] Bump io.freefair.lombok in /software/core/oqm-core-base-station Bumps [io.freefair.lombok](https://github.com/freefair/gradle-plugins) from 9.2.0 to 9.4.0. - [Release notes](https://github.com/freefair/gradle-plugins/releases) - [Commits](https://github.com/freefair/gradle-plugins/compare/9.2.0...9.4.0) --- updated-dependencies: - dependency-name: io.freefair.lombok dependency-version: 9.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- software/core/oqm-core-base-station/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/software/core/oqm-core-base-station/build.gradle b/software/core/oqm-core-base-station/build.gradle index b20eccf7d3..97c12e54c3 100644 --- a/software/core/oqm-core-base-station/build.gradle +++ b/software/core/oqm-core-base-station/build.gradle @@ -1,7 +1,7 @@ plugins { id 'java' id 'io.quarkus' - id "io.freefair.lombok" version "9.2.0" + id "io.freefair.lombok" version "9.4.0" } group 'com.ebp.openQuarterMaster' From d5085d3df780d00bf3e1c84e238b7ae275c288c9 Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Thu, 23 Apr 2026 23:56:00 -0400 Subject: [PATCH 17/38] Updated ext item search workflow --- .github/workflows/plugin-extItemSearch.yml | 39 ++++++---------------- 1 file changed, 10 insertions(+), 29 deletions(-) diff --git a/.github/workflows/plugin-extItemSearch.yml b/.github/workflows/plugin-extItemSearch.yml index 0f9d1b5767..68de80f035 100644 --- a/.github/workflows/plugin-extItemSearch.yml +++ b/.github/workflows/plugin-extItemSearch.yml @@ -1,41 +1,22 @@ name: CI - Plugin - External Item Search -# Controls when the workflow will run on: - # Triggers the workflow on push or pull request events but only for the main branch - push: - branches: [ "**" ] - paths: - - "software/plugins/external-item-search/**" - - ".github/workflows/plugin-extItemSearch.yml" pull_request: - branches: [ "**" ] paths: - "software/plugins/external-item-search/**" - ".github/workflows/plugin-extItemSearch.yml" workflow_call: - # Allows you to run this workflow manually from the Actions tab workflow_dispatch: -defaults: - run: - working-directory: "software/plugins/external-item-search/" -# A workflow run is made up of one or more jobs that can run sequentially or in parallel + +concurrency: + group: ci-plugin-extItemSearch-${{ github.ref }} + cancel-in-progress: true + jobs: - build: - uses: ./.github/workflows/wf-gradleBuild.yaml + CI-Pipeline: + uses: Epic-Breakfast-Productions/ebp-ci/.github/workflows/quarkus-ci-pipeline.yml@main with: path: "software/plugins/external-item-search/" - unitTest: - uses: ./.github/workflows/wf-gradleUnitTest.yaml - with: - path: "software/plugins/external-item-search/" - intTest: - uses: ./.github/workflows/wf-gradleQuarkusIntTest.yaml - strategy: - matrix: - containerBased: [ false ] # TODO:: enable true - with: - path: "software/plugins/external-item-search/" - containerBased: ${{ matrix.containerBased }} - - + java-version: "25" + run-int-tests: true + container-based-int-tests: true From db45e863819aa236831aba385e23769d9e976b35 Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Fri, 24 Apr 2026 00:00:24 -0400 Subject: [PATCH 18/38] Plugin - Ext Item Search - minor test tweaks --- software/plugins/external-item-search/build.gradle | 4 ++++ .../tech/ebp/oqm/plugin/extItemSearch/BootTestIT.java | 7 ++----- .../java/tech/ebp/openQuarterMaster/BootIT.java | 9 --------- 3 files changed, 6 insertions(+), 14 deletions(-) delete mode 100644 software/plugins/external-item-search/src/native-test/java/tech/ebp/openQuarterMaster/BootIT.java diff --git a/software/plugins/external-item-search/build.gradle b/software/plugins/external-item-search/build.gradle index 196132d9b6..f637088a95 100644 --- a/software/plugins/external-item-search/build.gradle +++ b/software/plugins/external-item-search/build.gradle @@ -55,6 +55,10 @@ compileTestJava { options.encoding = 'UTF-8' } +check { + finalizedBy quarkusIntTest +} + /** * Used to print the current version of this project. * diff --git a/software/plugins/external-item-search/src/integrationTest/java/tech/ebp/oqm/plugin/extItemSearch/BootTestIT.java b/software/plugins/external-item-search/src/integrationTest/java/tech/ebp/oqm/plugin/extItemSearch/BootTestIT.java index 66f7420dc1..64b68df596 100644 --- a/software/plugins/external-item-search/src/integrationTest/java/tech/ebp/oqm/plugin/extItemSearch/BootTestIT.java +++ b/software/plugins/external-item-search/src/integrationTest/java/tech/ebp/oqm/plugin/extItemSearch/BootTestIT.java @@ -1,10 +1,7 @@ package tech.ebp.oqm.plugin.extItemSearch; -import io.quarkus.test.junit.QuarkusTest; -import org.junit.jupiter.api.Test; +import io.quarkus.test.junit.QuarkusIntegrationTest; -import static io.restassured.RestAssured.given; - -@QuarkusTest +@QuarkusIntegrationTest class BootTestIT extends BootTest { } \ No newline at end of file diff --git a/software/plugins/external-item-search/src/native-test/java/tech/ebp/openQuarterMaster/BootIT.java b/software/plugins/external-item-search/src/native-test/java/tech/ebp/openQuarterMaster/BootIT.java deleted file mode 100644 index 55057d0f24..0000000000 --- a/software/plugins/external-item-search/src/native-test/java/tech/ebp/openQuarterMaster/BootIT.java +++ /dev/null @@ -1,9 +0,0 @@ -package tech.ebp.openQuarterMaster; - -import io.quarkus.test.junit.QuarkusIntegrationTest; -import tech.ebp.oqm.plugin.extItemSearch.BootTest; - -@QuarkusIntegrationTest -class BootIT extends BootTest { - // Execute the same tests but in packaged mode. -} From 7259f77eca46a1f94048a426a79565b77a085f47 Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Fri, 24 Apr 2026 21:41:42 -0400 Subject: [PATCH 19/38] Core - Base Station - Initial rework for new paradigm of ext item lookup --- .../core/oqm-core-base-station/build.gradle | 2 +- .../ItemLookupRestInterface.java | 76 ++++++------- .../service/ExternalItemSearchClient.java | 20 ++-- .../resources/res/js/item/ExtItemSearch.js | 100 ++++++++---------- .../templates/tags/inputs/barcodeInput.html | 6 +- .../templates/tags/itemAddEditModal.html | 75 +++++-------- .../templates/tags/providerInfoCard.html | 64 ++++++----- .../resources/templates/webui/pages/help.html | 33 +----- 8 files changed, 158 insertions(+), 218 deletions(-) diff --git a/software/core/oqm-core-base-station/build.gradle b/software/core/oqm-core-base-station/build.gradle index e7c9864181..6c73ac51c0 100644 --- a/software/core/oqm-core-base-station/build.gradle +++ b/software/core/oqm-core-base-station/build.gradle @@ -5,7 +5,7 @@ plugins { } group 'com.ebp.openQuarterMaster' -version '1.15.4' +version '1.15.5-SNAPSHOT' repositories { mavenCentral() diff --git a/software/core/oqm-core-base-station/src/main/java/tech/ebp/oqm/core/baseStation/interfaces/rest/passthrough/plugins/externalItemSearch/ItemLookupRestInterface.java b/software/core/oqm-core-base-station/src/main/java/tech/ebp/oqm/core/baseStation/interfaces/rest/passthrough/plugins/externalItemSearch/ItemLookupRestInterface.java index 93ae478bcc..878fecada7 100644 --- a/software/core/oqm-core-base-station/src/main/java/tech/ebp/oqm/core/baseStation/interfaces/rest/passthrough/plugins/externalItemSearch/ItemLookupRestInterface.java +++ b/software/core/oqm-core-base-station/src/main/java/tech/ebp/oqm/core/baseStation/interfaces/rest/passthrough/plugins/externalItemSearch/ItemLookupRestInterface.java @@ -1,23 +1,29 @@ package tech.ebp.oqm.core.baseStation.interfaces.rest.passthrough.plugins.externalItemSearch; -import com.fasterxml.jackson.databind.node.ObjectNode; import io.quarkus.security.Authenticated; import io.smallrye.mutiny.Uni; -import jakarta.annotation.security.PermitAll; import jakarta.enterprise.context.RequestScoped; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.ws.rs.BeanParam; +import jakarta.ws.rs.DefaultValue; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; +import lombok.NonNull; import lombok.extern.slf4j.Slf4j; +import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; import org.eclipse.microprofile.openapi.annotations.tags.Tag; import org.eclipse.microprofile.openapi.annotations.tags.Tags; import org.eclipse.microprofile.rest.client.inject.RestClient; import tech.ebp.oqm.core.baseStation.interfaces.rest.passthrough.PassthroughProvider; import tech.ebp.oqm.core.baseStation.service.ExternalItemSearchClient; +import java.util.List; + @Slf4j @Path(PassthroughProvider.PASSTHROUGH_API_PLUGIN_ROOT + "/itemLookup") @Tags({@Tag(name = "External Item Lookup", description = "Endpoints for searching for items from other places.")}) @@ -29,44 +35,40 @@ public class ItemLookupRestInterface extends PassthroughProvider { ExternalItemSearchClient externalItemSearchClient; @GET - @Path("/providers") + @Path("/search") @Produces(MediaType.APPLICATION_JSON) - public Uni allProviderInfo() { + public Uni search(@BeanParam ItemLookupRequest request) { return this.handleCall( - this.externalItemSearchClient.allProviderInfo() + this.externalItemSearchClient.search(request) ); } - @GET - @Path("barcode/{barcode}") - @Produces(MediaType.APPLICATION_JSON) - public Uni searchBarcode( - @PathParam("barcode") String barcode - ) { - return this.handleCall( - this.externalItemSearchClient.searchBarcode(barcode) - ); - } - - @GET - @Path("webpage-scrape/{webpage}") - @Produces(MediaType.APPLICATION_JSON) - public Uni scanWebpage( - @PathParam("webpage") String page - ) { - return this.handleCall( - this.externalItemSearchClient.scanWebpage(page) - ); - } - - @GET - @Path("lego/part/{partNo}") - @Produces(MediaType.APPLICATION_JSON) - public Uni searchLegoPart( - @PathParam("partNo") String partNo - ) { - return this.handleCall( - this.externalItemSearchClient.searchLegoPart(partNo) - ); + public static class ItemLookupRequest { + + @Parameter(description = "The type of lookup to perform. If empty, will perform a text search.") + @QueryParam("lookupMethod") + @DefaultValue("TEXT") + List lookupMethods; + + @Parameter(description = "The data source(s) to use to search. If empty, any are used.") + @QueryParam("service") + List services; + + @Parameter( + description = "The source(s) to use to search. Distinct from 'services', as some sources can pull from many services. Example, one service might present data from Amazon," + + " and other retailers. If empty, will search all available." + ) + @QueryParam("source") + List sources; + + @NonNull + @NotNull + @NotBlank + @QueryParam("q") + String search; + + @QueryParam("keepNotFound") + @DefaultValue("false") + boolean keepNotFound; } } \ No newline at end of file diff --git a/software/core/oqm-core-base-station/src/main/java/tech/ebp/oqm/core/baseStation/service/ExternalItemSearchClient.java b/software/core/oqm-core-base-station/src/main/java/tech/ebp/oqm/core/baseStation/service/ExternalItemSearchClient.java index 8da10f6974..ea0c833076 100644 --- a/software/core/oqm-core-base-station/src/main/java/tech/ebp/oqm/core/baseStation/service/ExternalItemSearchClient.java +++ b/software/core/oqm-core-base-station/src/main/java/tech/ebp/oqm/core/baseStation/service/ExternalItemSearchClient.java @@ -1,13 +1,14 @@ package tech.ebp.oqm.core.baseStation.service; -import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.ArrayNode; import io.smallrye.mutiny.Uni; +import jakarta.ws.rs.BeanParam; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; +import tech.ebp.oqm.core.baseStation.interfaces.rest.passthrough.plugins.externalItemSearch.ItemLookupRestInterface; @Path("/api/v1") @RegisterRestClient(configKey = "externalItemSearch") @@ -16,20 +17,11 @@ public interface ExternalItemSearchClient { @GET @Path("/providers") @Produces(MediaType.APPLICATION_JSON) - Uni allProviderInfo(); + Uni allProviderInfo(); @GET - @Path("barcode/{barcode}") + @Path("/search") @Produces(MediaType.APPLICATION_JSON) - Uni searchBarcode(@PathParam("barcode") String barcode); + Uni search(@BeanParam ItemLookupRestInterface.ItemLookupRequest request); - @GET - @Path("webpage-scrape/{webpage}") - @Produces(MediaType.APPLICATION_JSON) - Uni scanWebpage(@PathParam("webpage") String page); - - @GET - @Path("lego/part/{partNo}") - @Produces(MediaType.APPLICATION_JSON) - Uni searchLegoPart(@PathParam("partNo") String partNo); } diff --git a/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js b/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js index 6b84184b18..10a0b07916 100644 --- a/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js +++ b/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js @@ -9,18 +9,12 @@ import {Identifiers} from "../Identifiers.js"; import {PageUtility} from "../utilClasses/PageUtility.js"; export class ExtItemSearch extends PageUtility { - static extSearchResults= $("#extSearchResults"); + static addEditProductSearchPane = $("#addEditProductSearchPane"); - static prodBarcodeSearchForm = $("#prodBarcodeSearchForm"); - static legoPartNumSearchForm = $("#legoPartNumSearchForm"); - static websiteScanSearchForm = $("#websiteScanSearchForm"); - static extItemSearchSearchFormMessages = $("#extItemSearchSearchFormMessages"); + static searchForm = $("#addEditProductSearchForm"); - static prodBarcodeSearchBarcodeInput = $("#prodBarcodeSearchBarcodeInput"); - static legoPartNumSearchInput = $("#legoPartNumSearchInput"); - static websiteScanSearchInput = $("#websiteScanSearchInput"); + static extSearchResults= $("#extSearchResults"); - static addEditProductSearchPane = $("#addEditProductSearchPane"); static getUseButton(text) { let newButton = $(''); @@ -45,7 +39,12 @@ export class ExtItemSearch extends PageUtility { } else { valElement = $(e.target.parentElement.nextElementSibling) } - targetInput.val(valElement.text()); + + if(targetInput.jquery){ + targetInput.val(valElement.text()); + } else {//assuming overType + targetInput.setValue(valElement.text()); + } }); section.children("h6").append(useButton); } else { @@ -159,9 +158,23 @@ export class ExtItemSearch extends PageUtility { return true; } + static async handleErrResult(result){ + //TODO + } + static async handleNotFoundResult(result){ + //TODO + } + + static carouselNum = 0; - static async handleExtItemSearchResult(result){ + static async handleItemResult(result){ + //TODO:: promise for waiting on image load + //TODO:: fix image add/selecting + //TODO:: links + //TODO:: identifiers //TODO:: better formatting, method for filling out values + //TODO:: get display name for source name, list result # + //TODO:: move to use expanding table to display this let resultCard = $('

'); { let header = $('
'); @@ -172,9 +185,7 @@ export class ExtItemSearch extends PageUtility { resultMainBody.append(ExtItemSearch.createSearchResultSection("Name", result.unifiedName, ItemAddEdit.addEditItemNameInput)); resultMainBody.append(ExtItemSearch.createSearchResultSection("Description", result.description, ItemAddEdit.addEditItemDescriptionInput)); - /* TODO:: */ if (result.images.length) { - //TODO:: add minimum height/width, set unique car id let carouselId = "extSearchResultImgCarousel-" + ExtItemSearch.carouselNum++; let imagesSection = $('
  • Images:
  • '); @@ -196,7 +207,7 @@ export class ExtItemSearch extends PageUtility { let imgPromises = []; result.images.forEach(function (curImageLoc, i) { let curPromise = async function () { - console.log("Getting image " + i); + console.log("Getting image ", i); let imageData = await ExtItemSearch.getImageBase64FromUrl(curImageLoc); @@ -272,21 +283,30 @@ export class ExtItemSearch extends PageUtility { ExtItemSearch.extSearchResults.append(resultCard); } + static async handleExtItemSearchResult(result){ + switch (result.type) { + case "SUCCESS": + return ExtItemSearch.handleItemResult(result); + case "NO_RESULTS": + return ExtItemSearch.handleNotFoundResult(result); + case "ERROR": + return ExtItemSearch.handleErrResult(result); + } + } + static async handleExtItemSearchResults(results) { - console.log("Got Results! # results: " + results.results.length + " # errors: " + Object.keys(results.serviceErrs).length); + console.log("Got Results! # results: " + results.length); - if (results.results.length === 0) { + if (results.length === 0) { ExtItemSearch.extSearchResults.html("

    No Results!

    "); } + let resultPromises = []; - results.results.forEach(function (result) { + results.forEach(function (result) { resultPromises.push(ExtItemSearch.handleExtItemSearchResult(result)); } ); - for (const [service, error] of Object.entries(results.serviceErrs)) { - PageMessageUtils.addMessageToDiv(ExtItemSearch.extItemSearchSearchFormMessages, "danger", error, "Failed calling " + service); - } await Promise.all(resultPromises); console.log("Finished processing ext item search results."); } @@ -307,31 +327,17 @@ export class ExtItemSearch extends PageUtility { static { window.ExtItemSearch = this; - ExtItemSearch.websiteScanSearchForm.submit(function (event) { + ExtItemSearch.searchForm.on("submit", function (event) { event.preventDefault(); - let webpage = ExtItemSearch.websiteScanSearchInput.val(); - console.log("Scanning a web page: " + webpage); - ExtItemSearch.extSearchResults.html(""); + console.log("Performing external item search."); - Rest.call({ - url: Rest.passRoot + "/plugin/itemLookup/webpage/scrape/" + encodeURIComponent(webpage), - done: async function (data) { - await ExtItemSearch.handleExtItemSearchResults(data); - }, - failMessagesDiv: ExtItemSearch.extItemSearchSearchFormMessages - }); - }); - - ExtItemSearch.prodBarcodeSearchForm.submit(function (event) { - event.preventDefault(); - let barcodeText = ExtItemSearch.prodBarcodeSearchBarcodeInput.val(); - console.log("Searching for a barcode: ", barcodeText); - Identifiers.getNewIdentifierInput(ItemAddEdit.identifierInputContainer).val(barcodeText); - Identifiers.addIdentifier(ItemAddEdit.identifierInputContainer); ExtItemSearch.extSearchResults.html(""); + let formData = new FormData(event.target); + let params = new URLSearchParams(formData); + Rest.call({ - url: Rest.passRoot + "/plugin/itemLookup/barcode/" + barcodeText, + url: Rest.passRoot + "/plugin/itemLookup/search?" + params.toString(), done: async function (data) { await ExtItemSearch.handleExtItemSearchResults(data); }, @@ -339,19 +345,5 @@ export class ExtItemSearch extends PageUtility { }); }); - ExtItemSearch.legoPartNumSearchForm.submit(function (event) { - event.preventDefault(); - let partNumber = ExtItemSearch.legoPartNumSearchInput.val(); - console.log("Searching for a lego part: " + partNumber); - ExtItemSearch.extSearchResults.html(""); - - Rest.call({ - url: Rest.passRoot + "/plugin/itemLookup/lego/part/" + partNumber, - done: async function (data) { - await ExtItemSearch.handleExtItemSearchResults(data) - }, - failMessagesDiv: ExtItemSearch.extItemSearchSearchFormMessages - }); - }); } } diff --git a/software/core/oqm-core-base-station/src/main/resources/templates/tags/inputs/barcodeInput.html b/software/core/oqm-core-base-station/src/main/resources/templates/tags/inputs/barcodeInput.html index c7d39d5812..4a13b9abe5 100644 --- a/software/core/oqm-core-base-station/src/main/resources/templates/tags/inputs/barcodeInput.html +++ b/software/core/oqm-core-base-station/src/main/resources/templates/tags/inputs/barcodeInput.html @@ -1,5 +1,5 @@
    - - - {#if searchSubmit??}{/if} + + + {#if searchSubmit??}{/if}
    \ No newline at end of file diff --git a/software/core/oqm-core-base-station/src/main/resources/templates/tags/itemAddEditModal.html b/software/core/oqm-core-base-station/src/main/resources/templates/tags/itemAddEditModal.html index ceb084262d..601142fe23 100644 --- a/software/core/oqm-core-base-station/src/main/resources/templates/tags/itemAddEditModal.html +++ b/software/core/oqm-core-base-station/src/main/resources/templates/tags/itemAddEditModal.html @@ -23,57 +23,30 @@
    Search for items:
    -
    - -
    -
    -
    -
    - - {#inputs/barcodeInput id='prodBarcodeSearchBarcodeInput' searchSubmit=true /} -
    Scan in or enter a barcode on a product.
    -
    -
    -
    -
    -
    -
    - -
    - - -
    -
    Most legos have a number on them, it's that parts numbers.
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    - - -
    -
    Very limited number of supported sites.
    -
    -
    -
    -
    -
    -
    +
    +
    +
    + + +
    Specify none to use "any".
    +
    + +
    + + +
    + +
    + + {#inputs/barcodeInput id='addEditProductSearchTermInput' name='q' type='text' searchSubmit=true required=true /} +
    Scan in or enter a search term.
    +
    +
    diff --git a/software/core/oqm-core-base-station/src/main/resources/templates/tags/providerInfoCard.html b/software/core/oqm-core-base-station/src/main/resources/templates/tags/providerInfoCard.html index 6b46c954d8..cd84737c4b 100644 --- a/software/core/oqm-core-base-station/src/main/resources/templates/tags/providerInfoCard.html +++ b/software/core/oqm-core-base-station/src/main/resources/templates/tags/providerInfoCard.html @@ -1,27 +1,39 @@ -
    - -
      - {#if !providerInfo.get("cost").asText().isBlank()} -
    • - {#icons/icon icon='cash-stack'}{/icons/icon} {providerInfo.get("cost").asText()} -
    • - {/if} -
    • - {#icons/icon icon='globe'}{/icons/icon} {providerInfo.get("acceptsContributions").asBoolean() ? 'Accepts contributions' : 'No community input'} -
    • -
    • - {#if providerInfo.get("enabled").asBoolean()} - {#icons/icon icon='check-circle'}{/icons/icon} Enabled - {#else} - {#icons/icon icon='x-circle'}{/icons/icon} Disabled - {/if} -
    • - {#if !providerInfo.get("description").asText().isBlank()} -
    • {providerInfo.get("description").asText()}
    • - {/if} -
    +
    +
    + +
      + {#if ! providerInfo.get("cost").asText().isBlank()} +
    • + {#icons/icon icon = 'cash-stack' /} {providerInfo.get("cost").asText()} + + {#if providerInfo.get("enabled").asBoolean()} + {#icons/icon icon = 'check-circle' /} Enabled + {#else} + {#icons/icon icon = 'x-circle' /} Disabled + {/if} + +
    • + {/if} +
    • + {#if providerInfo.get("acceptsContributions").asBoolean()} + {#icons/icon icon = 'globe' /} Accepts contributions + {#else} + {#icons/icon icon = 'slash-circle' /} No community input + {/if} +
    • +
    • + Supported search methods:
      + {#for method in providerInfo.get("lookupMethods")} + {method.asText()} + {/for} +
    • + {#if ! providerInfo.get("description").asText().isBlank()} +
    • {providerInfo.get("description").asText()}
    • + {/if} +
    +
    \ No newline at end of file diff --git a/software/core/oqm-core-base-station/src/main/resources/templates/webui/pages/help.html b/software/core/oqm-core-base-station/src/main/resources/templates/webui/pages/help.html index d5b50ec5a4..a1ad7b396b 100644 --- a/software/core/oqm-core-base-station/src/main/resources/templates/webui/pages/help.html +++ b/software/core/oqm-core-base-station/src/main/resources/templates/webui/pages/help.html @@ -249,39 +249,8 @@

    {#if extSearchEnabled}
    -

    - Products -

    -

    - Services to search for Products by UPC/Barcode -

    - {#for productProvider in allProviderInfo.get("product")} - {#providerInfoCard providerInfo = productProvider /} - {/for} -
    -
    -

    - Websites -

    -

    - These websites are supported to glean data from. The intent is for you to enter a specific - product's page. -

    -
    - {#for productProvider in allProviderInfo.get("webpage")} - {#providerInfoCard providerInfo = productProvider /} - {/for} -
    -
    -

    - Lego -

    -

    - This describes the external services to search for Lego(tm) bricks. -

    -
    - {#for productProvider in allProviderInfo.get("lego")} + {#for productProvider in allProviderInfo} {#providerInfoCard providerInfo = productProvider /} {/for}
    From 7ff355f8451901096cf48cdf2956b40b981f7b0e Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Sat, 25 Apr 2026 01:10:51 -0400 Subject: [PATCH 20/38] Lib - Core API for Quarkus - Fixed issue searching for files and images --- software/libs/core-api-lib-quarkus/deployment/pom.xml | 2 +- software/libs/core-api-lib-quarkus/integration-tests/pom.xml | 2 +- software/libs/core-api-lib-quarkus/pom.xml | 2 +- software/libs/core-api-lib-quarkus/runtime/pom.xml | 2 +- .../runtime/restClient/searchObjects/FileSearchObject.java | 3 +++ .../quarkus/runtime/restClient/searchObjects/ImageSearch.java | 4 ++++ 6 files changed, 11 insertions(+), 4 deletions(-) diff --git a/software/libs/core-api-lib-quarkus/deployment/pom.xml b/software/libs/core-api-lib-quarkus/deployment/pom.xml index 84c04a62f3..8703ac2757 100644 --- a/software/libs/core-api-lib-quarkus/deployment/pom.xml +++ b/software/libs/core-api-lib-quarkus/deployment/pom.xml @@ -8,7 +8,7 @@ tech.epic-breakfast-productions.openquartermaster.lib.core core-api-lib-quarkus-parent - 4.4.5 + 4.4.6-SNAPSHOT core-api-lib-quarkus-deployment Core Api Lib Quarkus - Deployment diff --git a/software/libs/core-api-lib-quarkus/integration-tests/pom.xml b/software/libs/core-api-lib-quarkus/integration-tests/pom.xml index 1ff3dfb15e..5d0efc2132 100644 --- a/software/libs/core-api-lib-quarkus/integration-tests/pom.xml +++ b/software/libs/core-api-lib-quarkus/integration-tests/pom.xml @@ -5,7 +5,7 @@ tech.ebp.oqm.lib.core.api.quark core-api-lib-quarkus-parent - 4.4.5 + 4.4.6-SNAPSHOT core-api-lib-quarkus-integration-tests Core Api Lib Quarkus - Integration Tests diff --git a/software/libs/core-api-lib-quarkus/pom.xml b/software/libs/core-api-lib-quarkus/pom.xml index 447cef9080..3791e10a7a 100644 --- a/software/libs/core-api-lib-quarkus/pom.xml +++ b/software/libs/core-api-lib-quarkus/pom.xml @@ -7,7 +7,7 @@ 4.0.0 tech.epic-breakfast-productions.openquartermaster.lib.core core-api-lib-quarkus-parent - 4.4.5 + 4.4.6-SNAPSHOT pom Core Api Lib Quarkus - Parent The parent pom package for the Core API Library for Quarkus. diff --git a/software/libs/core-api-lib-quarkus/runtime/pom.xml b/software/libs/core-api-lib-quarkus/runtime/pom.xml index 37ee33f88f..c0cd1f36dc 100644 --- a/software/libs/core-api-lib-quarkus/runtime/pom.xml +++ b/software/libs/core-api-lib-quarkus/runtime/pom.xml @@ -8,7 +8,7 @@ tech.epic-breakfast-productions.openquartermaster.lib.core core-api-lib-quarkus-parent - 4.4.5 + 4.4.6-SNAPSHOT core-api-lib-quarkus Core Api Lib Quarkus - Runtime diff --git a/software/libs/core-api-lib-quarkus/runtime/src/main/java/tech/ebp/oqm/lib/core/api/quarkus/runtime/restClient/searchObjects/FileSearchObject.java b/software/libs/core-api-lib-quarkus/runtime/src/main/java/tech/ebp/oqm/lib/core/api/quarkus/runtime/restClient/searchObjects/FileSearchObject.java index 1359531cef..c60c206bd8 100644 --- a/software/libs/core-api-lib-quarkus/runtime/src/main/java/tech/ebp/oqm/lib/core/api/quarkus/runtime/restClient/searchObjects/FileSearchObject.java +++ b/software/libs/core-api-lib-quarkus/runtime/src/main/java/tech/ebp/oqm/lib/core/api/quarkus/runtime/restClient/searchObjects/FileSearchObject.java @@ -1,11 +1,14 @@ package tech.ebp.oqm.lib.core.api.quarkus.runtime.restClient.searchObjects; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.ToString; import lombok.experimental.SuperBuilder; @ToString(callSuper = true) @Getter @SuperBuilder(toBuilder = true) +@NoArgsConstructor public class FileSearchObject extends SearchObject { + } diff --git a/software/libs/core-api-lib-quarkus/runtime/src/main/java/tech/ebp/oqm/lib/core/api/quarkus/runtime/restClient/searchObjects/ImageSearch.java b/software/libs/core-api-lib-quarkus/runtime/src/main/java/tech/ebp/oqm/lib/core/api/quarkus/runtime/restClient/searchObjects/ImageSearch.java index bf75676f95..015159b6d5 100644 --- a/software/libs/core-api-lib-quarkus/runtime/src/main/java/tech/ebp/oqm/lib/core/api/quarkus/runtime/restClient/searchObjects/ImageSearch.java +++ b/software/libs/core-api-lib-quarkus/runtime/src/main/java/tech/ebp/oqm/lib/core/api/quarkus/runtime/restClient/searchObjects/ImageSearch.java @@ -2,11 +2,15 @@ import jakarta.ws.rs.QueryParam; import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; import lombok.ToString; import lombok.experimental.SuperBuilder; +@NoArgsConstructor @ToString(callSuper = true) @Getter +@Setter @SuperBuilder(toBuilder = true) public class ImageSearch extends FileSearchObject { @QueryParam("title") String imageTitle; From b99cf50b4d7fbc4b16581e0bc40a017d79d1d446 Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Sat, 25 Apr 2026 02:20:50 -0400 Subject: [PATCH 21/38] Core - Base Station - initial rework of ext item search results and general search --- .../core/oqm-core-base-station/build.gradle | 2 +- .../META-INF/resources/res/js/Identifiers.js | 17 +- .../resources/res/js/item/ExtItemSearch.js | 203 +++++++++++++++--- .../resources/res/js/obj/ObjViewUtils.js | 13 +- .../templates/tags/itemAddEditModal.html | 47 +++- 5 files changed, 230 insertions(+), 52 deletions(-) diff --git a/software/core/oqm-core-base-station/build.gradle b/software/core/oqm-core-base-station/build.gradle index 6c73ac51c0..153ae837c8 100644 --- a/software/core/oqm-core-base-station/build.gradle +++ b/software/core/oqm-core-base-station/build.gradle @@ -31,7 +31,7 @@ dependencies { implementation 'io.quarkus:quarkus-micrometer-opentelemetry' implementation 'io.quarkus:quarkus-scheduler' - implementation 'tech.epic-breakfast-productions.openquartermaster.lib.core:core-api-lib-quarkus:4.4.5' + implementation 'tech.epic-breakfast-productions.openquartermaster.lib.core:core-api-lib-quarkus:4.4.6-SNAPSHOT' implementation 'org.apache.commons:commons-io:1.3.2' implementation 'org.apache.commons:commons-text:1.15.0' diff --git a/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/Identifiers.js b/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/Identifiers.js index 1cc78fc5f4..49a4dc669a 100644 --- a/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/Identifiers.js +++ b/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/Identifiers.js @@ -55,13 +55,7 @@ export class Identifiers { Identifiers.clearInput(identifierInputContainerJq); Identifiers.getIdentifiersContainer(identifierInputContainerJq).html(""); } - static addIdentifier(identifierInputContainerJq) { - let newIdentifier = Identifiers.getNewIdentifierValue(identifierInputContainerJq); - if(newIdentifier === "") { - console.log("Not adding empty identifier."); - return; - } - + static addIdentifierFromValue(identifierInputContainerJq, newIdentifier){ console.log("Adding a new identifier: ", newIdentifier); return Rest.call({ @@ -77,6 +71,15 @@ export class Identifiers { } }); } + static addIdentifier(identifierInputContainerJq) { + let newIdentifier = Identifiers.getNewIdentifierValue(identifierInputContainerJq); + if(newIdentifier === "") { + console.log("Not adding empty identifier."); + return; + } + + return Identifiers.addIdentifierFromValue(identifierInputContainerJq, newIdentifier); + } static addToGenerate(generateIdButtonJq, generatorData) { let idContainer = Identifiers.getIdentifiersContainer(Identifiers.getInputContainer(generateIdButtonJq)); } diff --git a/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js b/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js index 10a0b07916..bb36ea6fb5 100644 --- a/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js +++ b/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js @@ -6,14 +6,32 @@ import {KeywordAttUtils} from "../obj/ObjViewUtils.js"; import {KeywordAttEdit} from "../obj/ObjEditUtils.js"; import {PageMessageUtils} from "../PageMessageUtils.js"; import {Identifiers} from "../Identifiers.js"; +import {Pricing} from "../Pricing.js"; import {PageUtility} from "../utilClasses/PageUtility.js"; +import {AssociatedLinks} from "../AssociatedLinks.js"; export class ExtItemSearch extends PageUtility { static addEditProductSearchPane = $("#addEditProductSearchPane"); static searchForm = $("#addEditProductSearchForm"); - static extSearchResults= $("#extSearchResults"); + static extSearchResultsContainer = $("#extSearchResults"); + static searchResultsCount = $("#extItemSearchSearchResultsTabNumResults"); + static searchResultsErrCount = $("#extItemSearchSearchResultsTabNumErrors"); + static extSearchResultsTableContent = $("#extItemSearchSearchResultsTableResults"); + static extItemSearchSearchFormMessages = $("#extItemSearchSearchFormMessages"); + + + static resetSearchResults() { + ExtItemSearch.extSearchResultsContainer.hide(); + ExtItemSearch.searchResultsCount.text("-"); + ExtItemSearch.searchResultsErrCount.text("-"); + ExtItemSearch.extSearchResultsTableContent.text(""); + } + + static sourceMethodToDisplay(sourceMethod) { + return sourceMethod;//TODO + } static getUseButton(text) { @@ -28,7 +46,7 @@ export class ExtItemSearch extends PageUtility { } static createSearchResultSection(name, value, targetInput) { - let section = $('
  • '); + let section = $('
    '); if (targetInput) { let useButton = ExtItemSearch.getUseButton(name); @@ -40,7 +58,7 @@ export class ExtItemSearch extends PageUtility { valElement = $(e.target.parentElement.nextElementSibling) } - if(targetInput.jquery){ + if (targetInput.jquery) { targetInput.val(valElement.text()); } else {//assuming overType targetInput.setValue(valElement.text()); @@ -54,6 +72,7 @@ export class ExtItemSearch extends PageUtility { let sectionText = $('

    '); sectionText.text(value); section.append(sectionText); + section.append($('
    ')); return section; } @@ -110,15 +129,15 @@ export class ExtItemSearch extends PageUtility { let imageName = resultUnifiedName; if (!data.length) { console.log("No results for given source. Adding."); - //TODO:: use image add form to add image, come back to this? + //TODO:: use image add form to add image? let saveImageFail = false; let filename = new URL(imageUrl).pathname; filename = filename.substring(filename.lastIndexOf('/') + 1); - if(filename.includes(".")){ + if (filename.includes(".")) { filename = filename.split('.').slice(0, -1).join('.') } - filename += "."+imageData.split(';')[0].split('/')[1]; + filename += "." + imageData.split(';')[0].split('/')[1]; let addData = new FormData(); addData.append("fileName", filename); @@ -158,36 +177,46 @@ export class ExtItemSearch extends PageUtility { return true; } - static async handleErrResult(result){ + static async handleErrResult(result) { //TODO } - static async handleNotFoundResult(result){ + + static async handleNotFoundResult(result) { //TODO } + static resultCount = 0; + + static getItemResultRow(result) { + let resultRow = $(''); + + resultRow.append($('').text(ExtItemSearch.resultCount++)); + resultRow.append($('').text(result.service));//TODO:: better name + resultRow.append($('').text(result.source));//TODO:: better name + resultRow.append($('').text(result.method));//TODO:: better name + resultRow.append($('').text(result.images.length)); + resultRow.append($('').text(Object.keys(result.prices).length)); + resultRow.append($('').text(Object.keys(result.links).length)); + resultRow.append($('').text(Object.keys(result.identifiers).length)); + resultRow.append($('')); + + return resultRow; + } static carouselNum = 0; - static async handleItemResult(result){ - //TODO:: promise for waiting on image load - //TODO:: fix image add/selecting - //TODO:: links - //TODO:: identifiers - //TODO:: better formatting, method for filling out values - //TODO:: get display name for source name, list result # - //TODO:: move to use expanding table to display this - let resultCard = $('
    '); - { - let header = $('
    '); - header.text(result.source); - resultCard.append(header); - } - let resultMainBody = $('
      '); + + static async getItemResultViewRow(result) { + //TODO:: promise for waiting on image load... need to diagnose this + + let resultViewRow = $(''); + let resultMainBody = $(''); + resultMainBody.append(ExtItemSearch.createSearchResultSection("Name", result.unifiedName, ItemAddEdit.addEditItemNameInput)); resultMainBody.append(ExtItemSearch.createSearchResultSection("Description", result.description, ItemAddEdit.addEditItemDescriptionInput)); if (result.images.length) { let carouselId = "extSearchResultImgCarousel-" + ExtItemSearch.carouselNum++; - let imagesSection = $('
    • Images:
    • '); + let imagesSection = $('
      Images:
      '); let carousel = $(' -
      -
      -
      - Search Results: -
      -
      -
      +
      +
      +
      + +
      +
      + + + + + + + + + + + + + + + +
      #ServiceSourceMethod{#icons/images /}{#icons/pricing /}{#icons/link /}{#icons/identifiers /}{#icons/view /}
      +
      +
      + foo +
      +
      +
      +
      From abcef28e73e351933e77d238045db1fccd81a817 Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Sat, 25 Apr 2026 02:29:19 -0400 Subject: [PATCH 22/38] Plugin - Ext Item Search - Tweaks, fixes, cleanup, and organization --- .../plugins/external-item-search/README.md | 15 ++++++- .../30-plugin-ext_item_search.json | 14 +++--- .../src/main/docker/Dockerfile.jvm | 2 +- .../src/main/docker/Dockerfile.legacy-jar | 2 +- .../interfaces/ItemLookupRestInterface.java | 7 --- .../lifecycle/LifecycleBean.java | 3 +- .../model/ExtItemLookupProviderInfo.java | 7 ++- .../extItemSearch/model/ExtItemSearch.java | 6 +-- .../lookupResult/ExtItemLookupResult.java | 11 +---- .../lookupResult/ExtItemLookupResults.java | 44 ------------------- .../model/lookupResult/LookupResult.java | 7 ++- .../service/ExtItemLookupService.java | 10 ++--- .../ExtItemSearchService.java | 6 --- .../ItemSearchService.java | 8 ++-- .../interfaces/BarcodeSearching.java | 5 --- .../interfaces/LegoSearching.java | 5 --- .../interfaces/TextSearching.java | 9 ---- .../interfaces/UrlSearching.java | 8 ---- .../barcodeLookup/BarcodeLookupClient.java | 2 +- .../barcodeLookup/BarcodeLookupService.java | 14 +++--- .../dataKick/DataKickLookupClient.java | 2 +- .../providers/dataKick/DatakickService.java | 13 +++--- .../rebrickable/RebrickableLookupClient.java | 2 +- .../rebrickable/RebrickableService.java | 16 +++---- .../upcItemDb/UpcItemDbLookupClient.java | 2 +- .../providers/upcItemDb/UpcItemDbService.java | 31 +++++++++---- .../utils/LookupMethod.java | 2 +- .../utils/LookupService.java | 12 ++--- .../utils/LookupSource.java | 2 +- .../utils/ResultMappingUtils.java | 2 +- .../src/test/resources/application.yaml | 2 +- 31 files changed, 98 insertions(+), 173 deletions(-) delete mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupResults.java delete mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/ExtItemSearchService.java rename software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/{searchServices => extItemSearchService}/ItemSearchService.java (91%) delete mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/BarcodeSearching.java delete mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/LegoSearching.java delete mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/TextSearching.java delete mode 100644 software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/UrlSearching.java rename software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/{searchServices => extItemSearchService}/providers/barcodeLookup/BarcodeLookupClient.java (89%) rename software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/{searchServices => extItemSearchService}/providers/barcodeLookup/BarcodeLookupService.java (91%) rename software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/{searchServices => extItemSearchService}/providers/dataKick/DataKickLookupClient.java (89%) rename software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/{searchServices => extItemSearchService}/providers/dataKick/DatakickService.java (88%) rename software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/{searchServices => extItemSearchService}/providers/rebrickable/RebrickableLookupClient.java (93%) rename software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/{searchServices => extItemSearchService}/providers/rebrickable/RebrickableService.java (93%) rename software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/{searchServices => extItemSearchService}/providers/upcItemDb/UpcItemDbLookupClient.java (92%) rename software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/{searchServices => extItemSearchService}/providers/upcItemDb/UpcItemDbService.java (83%) rename software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/{searchServices => extItemSearchService}/utils/LookupMethod.java (50%) rename software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/{searchServices => extItemSearchService}/utils/LookupService.java (63%) rename software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/{searchServices => extItemSearchService}/utils/LookupSource.java (53%) rename software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/{searchServices => extItemSearchService}/utils/ResultMappingUtils.java (86%) diff --git a/software/plugins/external-item-search/README.md b/software/plugins/external-item-search/README.md index 81fcafc3af..befdcd90d6 100644 --- a/software/plugins/external-item-search/README.md +++ b/software/plugins/external-item-search/README.md @@ -1,7 +1,20 @@ -# Open QuarterMAster External item lookup plugin +# External Item Search Plugin + + This service enables searching for items outside the OQM system, in order to get their data into it. +An aggregation service to amalgamate item data from multiple sources into a single format for use in OQM. + +Caches results from upstream API's to reduce usage where unnecessary. + + + + + + + + ## Quarkus info diff --git a/software/plugins/external-item-search/installerSrc/30-plugin-ext_item_search.json b/software/plugins/external-item-search/installerSrc/30-plugin-ext_item_search.json index 391d80c044..676005fe0e 100644 --- a/software/plugins/external-item-search/installerSrc/30-plugin-ext_item_search.json +++ b/software/plugins/external-item-search/installerSrc/30-plugin-ext_item_search.json @@ -7,19 +7,19 @@ "internalBaseUri": "http://#{plugin.externalItemSearch.internalHost}:#{plugin.externalItemSearch.internalHttpPort}", "services": { "barcodelookup": { - "enabled": true, - "apiKey": null + "enabled": "true", + "apiKey": " " }, "datakick": { - "enabled": true, + "enabled": "true" }, "rebrickable": { - "enabled": true, - "apiKey": null + "enabled": "true", + "apiKey": " " }, "upcitemdb": { - "enabled": true, - "apiKey": null + "enabled": "true", + "apiKey": " " } } } diff --git a/software/plugins/external-item-search/src/main/docker/Dockerfile.jvm b/software/plugins/external-item-search/src/main/docker/Dockerfile.jvm index 6c2aa52095..38c9df2c85 100644 --- a/software/plugins/external-item-search/src/main/docker/Dockerfile.jvm +++ b/software/plugins/external-item-search/src/main/docker/Dockerfile.jvm @@ -77,7 +77,7 @@ # accessed directly. (example: "foo.example.com,bar.example.com") # ### -FROM registry.access.redhat.com/ubi8/openjdk-17:1.18 +FROM registry.access.redhat.com/ubi9/openjdk-25:1.24 ENV LANGUAGE='en_US:en' diff --git a/software/plugins/external-item-search/src/main/docker/Dockerfile.legacy-jar b/software/plugins/external-item-search/src/main/docker/Dockerfile.legacy-jar index 52d24ae352..dc11682b5f 100644 --- a/software/plugins/external-item-search/src/main/docker/Dockerfile.legacy-jar +++ b/software/plugins/external-item-search/src/main/docker/Dockerfile.legacy-jar @@ -77,7 +77,7 @@ # accessed directly. (example: "foo.example.com,bar.example.com") # ### -FROM registry.access.redhat.com/ubi8/openjdk-17:1.18 +FROM registry.access.redhat.com/ubi9/openjdk-25:1.24 ENV LANGUAGE='en_US:en' diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java index 48227d777b..1d33dce6fc 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java @@ -8,10 +8,8 @@ import jakarta.ws.rs.BeanParam; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; -import jakarta.ws.rs.PathParam; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; import lombok.extern.slf4j.Slf4j; import org.eclipse.microprofile.openapi.annotations.Operation; import org.eclipse.microprofile.openapi.annotations.enums.SchemaType; @@ -22,15 +20,10 @@ import org.eclipse.microprofile.openapi.annotations.tags.Tags; import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemSearch; -import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResults; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; -import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ResultType; import tech.ebp.oqm.plugin.extItemSearch.service.ExtItemLookupService; -import java.net.MalformedURLException; -import java.net.URL; import java.util.List; -import java.util.concurrent.ExecutionException; @Slf4j @Path("/api/v1") diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/lifecycle/LifecycleBean.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/lifecycle/LifecycleBean.java index bcc9c57fcd..1ade51d674 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/lifecycle/LifecycleBean.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/lifecycle/LifecycleBean.java @@ -7,9 +7,8 @@ import jakarta.inject.Singleton; import lombok.extern.slf4j.Slf4j; import org.eclipse.microprofile.config.ConfigProvider; -import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; import tech.ebp.oqm.plugin.extItemSearch.service.ExtItemLookupService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.ItemSearchService; import java.time.Duration; import java.time.ZonedDateTime; diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupProviderInfo.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupProviderInfo.java index f3d4db9d5b..88ae098f14 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupProviderInfo.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemLookupProviderInfo.java @@ -2,13 +2,12 @@ import jakarta.validation.constraints.NotNull; import lombok.*; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupMethod; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupSource; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupMethod; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupService; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupSource; import java.net.URI; import java.util.Collection; -import java.util.List; /** * Information about an external item lookup provider diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemSearch.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemSearch.java index 8ef368e771..cf1e25431b 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemSearch.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/ExtItemSearch.java @@ -13,9 +13,9 @@ import lombok.Setter; import lombok.ToString; import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupMethod; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupSource; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupMethod; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupService; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupSource; import java.util.List; diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupResult.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupResult.java index e765d5a13c..a34d347c80 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupResult.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupResult.java @@ -58,20 +58,11 @@ public ResultType getType() { @NonNull @NotNull @lombok.Builder.Default - private List<@NotNull @NonNull @NotBlank String> images = new ArrayList<>(); + private List images = new ArrayList<>(); @NonNull @NotNull @lombok.Builder.Default private Map links = new HashMap<>(); - public ExtItemLookupResult addAttIfNotBlank(String key, String val) { - if ( - key != null && !key.isBlank() && - val != null && !val.isBlank() - ) { - this.getAttributes().put(key, val); - } - return this; - } } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupResults.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupResults.java deleted file mode 100644 index 43f2846c25..0000000000 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/ExtItemLookupResults.java +++ /dev/null @@ -1,44 +0,0 @@ -package tech.ebp.oqm.plugin.extItemSearch.model.lookupResult; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.NonNull; -import lombok.experimental.SuperBuilder; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -@Deprecated -@Data -@AllArgsConstructor -@NoArgsConstructor -@SuperBuilder -public class ExtItemLookupResults { - - @NonNull - @NotNull - @lombok.Builder.Default - private List results = new ArrayList<>(); - - @NonNull - @NotNull - @lombok.Builder.Default - private Map<@NonNull @NotNull @NotBlank String, String> serviceErrs = new HashMap<>(); - - public ExtItemLookupResults combine(ExtItemLookupResults other) { - ExtItemLookupResults output = new ExtItemLookupResults(); - - output.getResults().addAll(this.getResults()); - output.getResults().addAll(other.getResults()); - - output.getServiceErrs().putAll(this.getServiceErrs()); - output.getServiceErrs().putAll(other.getServiceErrs()); - - return this; - } -} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/LookupResult.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/LookupResult.java index b048aac176..0d3da3249c 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/LookupResult.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/model/lookupResult/LookupResult.java @@ -2,16 +2,15 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; -import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.NonNull; import lombok.experimental.SuperBuilder; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupMethod; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupSource; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupMethod; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupService; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupSource; @Data @AllArgsConstructor diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/ExtItemLookupService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/ExtItemLookupService.java index 9c2697e5e4..42357eb6e3 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/ExtItemLookupService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/ExtItemLookupService.java @@ -8,11 +8,11 @@ import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemSearch; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ResultType; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.barcodeLookup.BarcodeLookupService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.dataKick.DatakickService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.rebrickable.RebrickableService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.upcItemDb.UpcItemDbService; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.ItemSearchService; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.providers.barcodeLookup.BarcodeLookupService; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.providers.dataKick.DatakickService; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.providers.rebrickable.RebrickableService; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.providers.upcItemDb.UpcItemDbService; import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; import java.util.*; diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/ExtItemSearchService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/ExtItemSearchService.java deleted file mode 100644 index e9909fd7cb..0000000000 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/ExtItemSearchService.java +++ /dev/null @@ -1,6 +0,0 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService; - -public abstract class ExtItemSearchService { - - -} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/ItemSearchService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/ItemSearchService.java similarity index 91% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/ItemSearchService.java rename to software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/ItemSearchService.java index 80d61a7265..1ecfe46c33 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/ItemSearchService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/ItemSearchService.java @@ -1,4 +1,4 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices; +package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService; import io.smallrye.mutiny.Multi; @@ -10,9 +10,9 @@ import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupErrResult; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupMethod; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupSource; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupMethod; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupService; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupSource; import java.util.ArrayList; import java.util.Collection; diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/BarcodeSearching.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/BarcodeSearching.java deleted file mode 100644 index 7457a64724..0000000000 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/BarcodeSearching.java +++ /dev/null @@ -1,5 +0,0 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.interfaces; - -public interface BarcodeSearching { - -} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/LegoSearching.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/LegoSearching.java deleted file mode 100644 index da4d0533dd..0000000000 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/LegoSearching.java +++ /dev/null @@ -1,5 +0,0 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.interfaces; - -public interface LegoSearching { - -} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/TextSearching.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/TextSearching.java deleted file mode 100644 index 196f30b38c..0000000000 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/TextSearching.java +++ /dev/null @@ -1,9 +0,0 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.interfaces; - -import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResults; - -public interface TextSearching { - - ExtItemLookupResults searchText(String url) throws Exception; - -} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/UrlSearching.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/UrlSearching.java deleted file mode 100644 index 7fbdc16780..0000000000 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/interfaces/UrlSearching.java +++ /dev/null @@ -1,8 +0,0 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.interfaces; - -import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResults; - -public interface UrlSearching { - - ExtItemLookupResults searchUrl(String url) throws Exception; -} diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupClient.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/providers/barcodeLookup/BarcodeLookupClient.java similarity index 89% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupClient.java rename to software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/providers/barcodeLookup/BarcodeLookupClient.java index fa0ca8d5f0..0d8bb70c8d 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupClient.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/providers/barcodeLookup/BarcodeLookupClient.java @@ -1,4 +1,4 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.barcodeLookup; +package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.providers.barcodeLookup; import com.fasterxml.jackson.databind.node.ObjectNode; import io.opentelemetry.instrumentation.annotations.WithSpan; diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/providers/barcodeLookup/BarcodeLookupService.java similarity index 91% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupService.java rename to software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/providers/barcodeLookup/BarcodeLookupService.java index 0f61821d9a..c7d7fe267c 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/barcodeLookup/BarcodeLookupService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/providers/barcodeLookup/BarcodeLookupService.java @@ -1,4 +1,4 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.barcodeLookup; +package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.providers.barcodeLookup; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; @@ -6,8 +6,6 @@ import io.smallrye.mutiny.Multi; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import lombok.Getter; -import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.rest.client.inject.RestClient; @@ -16,11 +14,11 @@ import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResult; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResultNoResults; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupMethod; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupSource; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.ResultMappingUtils; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.ItemSearchService; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupMethod; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupService; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupSource; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.ResultMappingUtils; import java.net.URI; import java.util.*; diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DataKickLookupClient.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/providers/dataKick/DataKickLookupClient.java similarity index 89% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DataKickLookupClient.java rename to software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/providers/dataKick/DataKickLookupClient.java index 3919f27bea..d6469e705d 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DataKickLookupClient.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/providers/dataKick/DataKickLookupClient.java @@ -1,4 +1,4 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.dataKick; +package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.providers.dataKick; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/providers/dataKick/DatakickService.java similarity index 88% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java rename to software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/providers/dataKick/DatakickService.java index 99e9a45efd..a5defdef27 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/dataKick/DatakickService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/providers/dataKick/DatakickService.java @@ -1,14 +1,11 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.dataKick; +package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.providers.dataKick; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; -import io.opentelemetry.instrumentation.annotations.WithSpan; import io.smallrye.mutiny.Multi; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import lombok.Getter; -import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.rest.client.inject.RestClient; @@ -17,10 +14,10 @@ import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResult; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResultNoResults; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupMethod; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupSource; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.ItemSearchService; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupMethod; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupService; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupSource; import java.net.URI; import java.util.*; diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableLookupClient.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/providers/rebrickable/RebrickableLookupClient.java similarity index 93% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableLookupClient.java rename to software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/providers/rebrickable/RebrickableLookupClient.java index a84aff6542..e48abd3373 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableLookupClient.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/providers/rebrickable/RebrickableLookupClient.java @@ -1,4 +1,4 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.rebrickable; +package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.providers.rebrickable; import com.fasterxml.jackson.databind.node.ObjectNode; import io.opentelemetry.instrumentation.annotations.WithSpan; diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/providers/rebrickable/RebrickableService.java similarity index 93% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java rename to software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/providers/rebrickable/RebrickableService.java index 09286c260d..654a432739 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/rebrickable/RebrickableService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/providers/rebrickable/RebrickableService.java @@ -1,14 +1,12 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.rebrickable; +package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.providers.rebrickable; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; -import io.opentelemetry.instrumentation.annotations.WithSpan; import io.smallrye.mutiny.Multi; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.rest.client.inject.RestClient; @@ -17,11 +15,11 @@ import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResult; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResultNoResults; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupMethod; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupSource; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.ResultMappingUtils; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.ItemSearchService; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupMethod; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupService; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupSource; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.ResultMappingUtils; import java.io.InputStream; import java.net.URI; @@ -73,7 +71,7 @@ public RebrickableService( .builder() .displayName("Rebrickable") .description("A database of LEGO(TM) pieces. Free, but requires you to get your own key.") - .acceptsContributions(false) + .acceptsContributions(true) .homepage(URI.create("https://rebrickable.com")) .cost("Free") ); diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/upcItemDb/UpcItemDbLookupClient.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/providers/upcItemDb/UpcItemDbLookupClient.java similarity index 92% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/upcItemDb/UpcItemDbLookupClient.java rename to software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/providers/upcItemDb/UpcItemDbLookupClient.java index 0c2437c43e..97229f04b4 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/upcItemDb/UpcItemDbLookupClient.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/providers/upcItemDb/UpcItemDbLookupClient.java @@ -1,4 +1,4 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.upcItemDb; +package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.providers.upcItemDb; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/upcItemDb/UpcItemDbService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/providers/upcItemDb/UpcItemDbService.java similarity index 83% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/upcItemDb/UpcItemDbService.java rename to software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/providers/upcItemDb/UpcItemDbService.java index 5be17dc475..3d0c940f05 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/providers/upcItemDb/UpcItemDbService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/providers/upcItemDb/UpcItemDbService.java @@ -1,4 +1,4 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.upcItemDb; +package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.providers.upcItemDb; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ArrayNode; @@ -16,17 +16,15 @@ import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.ExtItemLookupResult; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResultNoResults; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupMethod; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupSource; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.ResultMappingUtils; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.ItemSearchService; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupMethod; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupService; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupSource; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.ResultMappingUtils; import java.net.URI; import java.util.*; -import static tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils.LookupMethod.BARCODE; - /** * * - Docs: https://www.upcitemdb.com/wp/docs/main/development/getting-started/ - Trial docs: https://www.upcitemdb.com/api/explorer#!/lookup/get_trial_lookup @@ -76,6 +74,8 @@ private ExtItemLookupResult jsonToResult(LookupSource source, LookupMethod metho Map attributes = new HashMap<>(); Map identifiers = new HashMap<>(); + Map links = new HashMap<>(); + Map prices = new HashMap<>(); List images = new ArrayList<>(); for (Map.Entry curField : json.properties()) { @@ -105,6 +105,19 @@ private ExtItemLookupResult jsonToResult(LookupSource source, LookupMethod metho images.add(curImg.asText()); } break; + case "offers": + for (JsonNode curOffer : curFieldVal) { + String name = curOffer.get("merchant").asText(); + + if(!ResultMappingUtils.isFieldEmpty(curOffer.get("link"))){ + links.put(name, curOffer.get("link").asText()); + } + + if(!ResultMappingUtils.isFieldEmpty(curOffer.get("price"))){ + prices.put(name, curOffer.get("price").asText()); + } + } + break; default: if (curFieldVal.isTextual()) { attributes.put(curFieldName, curFieldVal.asText()); @@ -117,6 +130,8 @@ private ExtItemLookupResult jsonToResult(LookupSource source, LookupMethod metho .attributes(attributes) .identifiers(identifiers) .images(images) + .links(links) + .prices(prices) .build(); } diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupMethod.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/utils/LookupMethod.java similarity index 50% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupMethod.java rename to software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/utils/LookupMethod.java index 7df588f976..420897e09f 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupMethod.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/utils/LookupMethod.java @@ -1,4 +1,4 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils; +package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils; public enum LookupMethod { TEXT, diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/utils/LookupService.java similarity index 63% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupService.java rename to software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/utils/LookupService.java index d64becf2bb..b21a05a93d 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/utils/LookupService.java @@ -1,10 +1,10 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils; +package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.ItemSearchService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.barcodeLookup.BarcodeLookupService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.dataKick.DatakickService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.rebrickable.RebrickableService; -import tech.ebp.oqm.plugin.extItemSearch.service.searchServices.providers.upcItemDb.UpcItemDbService; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.ItemSearchService; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.providers.barcodeLookup.BarcodeLookupService; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.providers.dataKick.DatakickService; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.providers.rebrickable.RebrickableService; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.providers.upcItemDb.UpcItemDbService; import java.util.Collection; import java.util.List; diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupSource.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/utils/LookupSource.java similarity index 53% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupSource.java rename to software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/utils/LookupSource.java index 652ae9a500..d7c3a81a4e 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/LookupSource.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/utils/LookupSource.java @@ -1,4 +1,4 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils; +package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils; public enum LookupSource { BARCODE_LOOKUP, diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/ResultMappingUtils.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/utils/ResultMappingUtils.java similarity index 86% rename from software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/ResultMappingUtils.java rename to software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/utils/ResultMappingUtils.java index a1560bff1c..f38a29d092 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/searchServices/utils/ResultMappingUtils.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/utils/ResultMappingUtils.java @@ -1,4 +1,4 @@ -package tech.ebp.oqm.plugin.extItemSearch.service.searchServices.utils; +package tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils; import com.fasterxml.jackson.databind.JsonNode; diff --git a/software/plugins/external-item-search/src/test/resources/application.yaml b/software/plugins/external-item-search/src/test/resources/application.yaml index 53d3f00825..5b1ed6e6ab 100644 --- a/software/plugins/external-item-search/src/test/resources/application.yaml +++ b/software/plugins/external-item-search/src/test/resources/application.yaml @@ -14,7 +14,7 @@ productLookup: quarkus: http: - port: 8081 +# port: 8081 log: level: DEBUG wiremock: From 4d6d1398ab038be24bdea403a832132ee2ff46a6 Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Sat, 25 Apr 2026 02:31:52 -0400 Subject: [PATCH 23/38] Plugin - Ext Item Search - Adding github workflow shield to readme --- software/plugins/external-item-search/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/software/plugins/external-item-search/README.md b/software/plugins/external-item-search/README.md index befdcd90d6..c272f7a7f5 100644 --- a/software/plugins/external-item-search/README.md +++ b/software/plugins/external-item-search/README.md @@ -1,6 +1,6 @@ # External Item Search Plugin - +![Plugin - External Item Search](https://github.com/Epic-Breakfast-Productions/OpenQuarterMaster/actions/workflows/plugin-extItemSearch.yml/badge.svg) This service enables searching for items outside the OQM system, in order to get their data into it. From 92059883f570f8832ed0dc8b8905c5b2bffa9c3e Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Sat, 25 Apr 2026 02:33:24 -0400 Subject: [PATCH 24/38] Updating dependabot formatting, updating project dev flow docs --- .github/dependabot.yml | 55 ++++++++++++++++++----------- docs/development/developmentFlow.md | 12 +++++++ 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d201463afe..da89d651c2 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,9 +5,12 @@ version: 2 updates: -# + +############################################################################### +############################################################################### # Core components -# +############################################################################### +############################################################################### # Core API - package-ecosystem: "gradle" @@ -48,10 +51,12 @@ updates: labels: - "area-dependencies" - "project-core-baseStation" - -# + +############################################################################### +############################################################################### # Libraries -# +############################################################################### +############################################################################### # Core API Lib Quarkus - package-ecosystem: "gradle" @@ -62,12 +67,16 @@ updates: day: "tuesday" labels: - "area-dependencies" - -# + +############################################################################### +############################################################################### # Plugins -# +############################################################################### +############################################################################### + # # External Item Search + # - package-ecosystem: "gradle" directory: "/software/plugins/external-item-search" target-branch: "development" @@ -76,7 +85,21 @@ updates: day: "tuesday" labels: - "area-dependencies" + - "project-plugin-extItemSearch" + - + package-ecosystem: "docker" + directory: "/software/plugins/external-item-search/src/main/docker" + target-branch: "development" + schedule: + interval: "weekly" + day: "tuesday" + labels: + - "area-dependencies" + - "project-plugin-extItemSearch" + + # # Mss Controller + # - package-ecosystem: "gradle" directory: "/software/plugins/mss-controller" target-branch: "development" @@ -85,7 +108,10 @@ updates: day: "tuesday" labels: - "area-dependencies" + + # # Storagotchi + # - package-ecosystem: "gradle" directory: "/software/plugins/storagotchi" target-branch: "development" @@ -103,15 +129,4 @@ updates: day: "tuesday" labels: - "area-dependencies" - - "project-core-api" - - # Demo -# - package-ecosystem: "gradle" -# directory: "/software/plugins/open-qm-plugin-demo" -# target-branch: "dev" -# schedule: -# interval: "weekly" -# labels: -# - "area-dependencies" -# - "project-plugins" - + - "project-plugin-storagotchi" diff --git a/docs/development/developmentFlow.md b/docs/development/developmentFlow.md index 558282fde5..38c9758d49 100644 --- a/docs/development/developmentFlow.md +++ b/docs/development/developmentFlow.md @@ -14,3 +14,15 @@ Branches: - final review into `main` - `dev/[issue #]-[issue name]` - where work happens + +## Github Project + +https://github.com/orgs/Epic-Breakfast-Productions/projects/8 + +All issues are to be assigned to the OQM github project for tracking purposes. We use the features of the project interface to track issues, assign them to team members, and manage workflows. + +## Labels + +https://github.com/Epic-Breakfast-Productions/OpenQuarterMaster/labels + +Labels help us categorize and organize issues. These should be accurate and applied as relevant. From 037f086939cc9ab3e67f6d38d4fe661ca5beb426 Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Sun, 26 Apr 2026 00:14:56 -0400 Subject: [PATCH 25/38] Core - Base Station - Now properly displaying errors, other polish --- .../interfaces/ui/pages/HelpUi.java | 9 +- .../interfaces/ui/pages/InventoryItemUi.java | 17 +- .../service/ExternalItemSearchClient.java | 10 +- .../resources/res/js/item/ExtItemSearch.js | 175 +++++++++++------- .../templates/tags/itemAddEditModal.html | 22 +-- .../templates/webui/pages/items.html | 2 +- 6 files changed, 144 insertions(+), 91 deletions(-) diff --git a/software/core/oqm-core-base-station/src/main/java/tech/ebp/oqm/core/baseStation/interfaces/ui/pages/HelpUi.java b/software/core/oqm-core-base-station/src/main/java/tech/ebp/oqm/core/baseStation/interfaces/ui/pages/HelpUi.java index 720e08d4c6..b3266d4502 100644 --- a/software/core/oqm-core-base-station/src/main/java/tech/ebp/oqm/core/baseStation/interfaces/ui/pages/HelpUi.java +++ b/software/core/oqm-core-base-station/src/main/java/tech/ebp/oqm/core/baseStation/interfaces/ui/pages/HelpUi.java @@ -55,10 +55,11 @@ public Uni overview() throws MalformedURLException { "unitCategoryMap", this.coreApiClient.unitGetAll(this.getBearerHeaderStr()), "extSearchEnabled", Uni.createFrom().item(extSearchEnabled), "allProviderInfo", extSearchEnabled ? - QuarkusRestClientBuilder.newBuilder() - .baseUrl(new URL(this.extSearchUrl)) - .build(ExternalItemSearchClient.class).allProviderInfo() - : Uni.createFrom().nullItem() + QuarkusRestClientBuilder.newBuilder() + .baseUrl(new URL(this.extSearchUrl)) + .build(ExternalItemSearchClient.class).allProviderInfo() + : Uni.createFrom().nullItem() + ) ); } diff --git a/software/core/oqm-core-base-station/src/main/java/tech/ebp/oqm/core/baseStation/interfaces/ui/pages/InventoryItemUi.java b/software/core/oqm-core-base-station/src/main/java/tech/ebp/oqm/core/baseStation/interfaces/ui/pages/InventoryItemUi.java index b612c49ff9..c694317093 100644 --- a/software/core/oqm-core-base-station/src/main/java/tech/ebp/oqm/core/baseStation/interfaces/ui/pages/InventoryItemUi.java +++ b/software/core/oqm-core-base-station/src/main/java/tech/ebp/oqm/core/baseStation/interfaces/ui/pages/InventoryItemUi.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import io.quarkus.qute.Location; import io.quarkus.qute.Template; +import io.quarkus.rest.client.reactive.QuarkusRestClientBuilder; import io.smallrye.mutiny.Uni; import jakarta.annotation.security.RolesAllowed; import jakarta.enterprise.context.RequestScoped; @@ -19,6 +20,7 @@ import org.eclipse.microprofile.openapi.annotations.tags.Tag; import org.eclipse.microprofile.openapi.annotations.tags.Tags; import org.eclipse.microprofile.rest.client.inject.RestClient; +import tech.ebp.oqm.core.baseStation.service.ExternalItemSearchClient; import tech.ebp.oqm.core.baseStation.service.modelTweak.SearchResultTweak; import tech.ebp.oqm.core.baseStation.utils.Roles; import tech.ebp.oqm.lib.core.api.quarkus.runtime.restClient.OqmCoreApiClientService; @@ -26,6 +28,8 @@ import tech.ebp.oqm.lib.core.api.quarkus.runtime.restClient.searchObjects.ItemCategorySearch; import tech.ebp.oqm.lib.core.api.quarkus.runtime.restClient.searchObjects.StorageBlockSearch; +import java.net.MalformedURLException; +import java.net.URL; import java.util.Map; @Slf4j @@ -50,10 +54,14 @@ public class InventoryItemUi extends UiProvider { @Inject SearchResultTweak searchResultTweak; + @ConfigProperty(name = "quarkus.rest-client.externalItemSearch.url", defaultValue = " ") + String extSearchUrl; + @GET @Path("items") @RolesAllowed(Roles.INVENTORY_VIEW) - public Uni itemsPage(@BeanParam InventoryItemSearch search) { + public Uni itemsPage(@BeanParam InventoryItemSearch search) throws MalformedURLException { + boolean extSearchEnabled = !this.extSearchUrl.isBlank(); this.ensureSearchDefaults(search); return this.getUni( @@ -63,7 +71,12 @@ public Uni itemsPage(@BeanParam InventoryItemSearch search) { "allCategorySearchResults", this.coreApiClient.itemCatSearch(this.getBearerHeaderStr(), this.getSelectedDb(), new ItemCategorySearch()), "searchResults", this.coreApiClient.invItemSearch(this.getBearerHeaderStr(), this.getSelectedDb(), search) .call(results->searchResultTweak.addCategoriesObjectsToSearchResult(results, this.getSelectedDb(), "categories", this.getBearerHeaderStr())), - "allUnitMap", this.coreApiClient.unitGetAll(this.getBearerHeaderStr()) + "allUnitMap", this.coreApiClient.unitGetAll(this.getBearerHeaderStr()), + "extSearchMethods", extSearchEnabled ? + QuarkusRestClientBuilder.newBuilder() + .baseUrl(new URL(this.extSearchUrl)) + .build(ExternalItemSearchClient.class).allMethodInfo() + : Uni.createFrom().nullItem() ) ); } diff --git a/software/core/oqm-core-base-station/src/main/java/tech/ebp/oqm/core/baseStation/service/ExternalItemSearchClient.java b/software/core/oqm-core-base-station/src/main/java/tech/ebp/oqm/core/baseStation/service/ExternalItemSearchClient.java index ea0c833076..98eed93f01 100644 --- a/software/core/oqm-core-base-station/src/main/java/tech/ebp/oqm/core/baseStation/service/ExternalItemSearchClient.java +++ b/software/core/oqm-core-base-station/src/main/java/tech/ebp/oqm/core/baseStation/service/ExternalItemSearchClient.java @@ -1,6 +1,7 @@ package tech.ebp.oqm.core.baseStation.service; import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import io.smallrye.mutiny.Uni; import jakarta.ws.rs.BeanParam; import jakarta.ws.rs.GET; @@ -13,11 +14,16 @@ @Path("/api/v1") @RegisterRestClient(configKey = "externalItemSearch") public interface ExternalItemSearchClient { - + @GET - @Path("/providers") + @Path("/info/providers") @Produces(MediaType.APPLICATION_JSON) Uni allProviderInfo(); + + @GET + @Path("/info/methods") + @Produces(MediaType.APPLICATION_JSON) + Uni allMethodInfo(); @GET @Path("/search") diff --git a/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js b/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js index bb36ea6fb5..540ff26b81 100644 --- a/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js +++ b/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js @@ -17,7 +17,9 @@ export class ExtItemSearch extends PageUtility { static extSearchResultsContainer = $("#extSearchResults"); static searchResultsCount = $("#extItemSearchSearchResultsTabNumResults"); + static searchResultsErrTab = $("#extItemSearchSearchResultsTabErrorsTab"); static searchResultsErrCount = $("#extItemSearchSearchResultsTabNumErrors"); + static searchResultsErrContent = $("#extItemSearchSearchResultsTabErrorsTabContent"); static extSearchResultsTableContent = $("#extItemSearchSearchResultsTableResults"); static extItemSearchSearchFormMessages = $("#extItemSearchSearchFormMessages"); @@ -27,10 +29,18 @@ export class ExtItemSearch extends PageUtility { ExtItemSearch.searchResultsCount.text("-"); ExtItemSearch.searchResultsErrCount.text("-"); ExtItemSearch.extSearchResultsTableContent.text(""); + ExtItemSearch.searchResultsErrContent.text(""); + ExtItemSearch.searchResultsErrTab.prop("disabled", true); } - static sourceMethodToDisplay(sourceMethod) { - return sourceMethod;//TODO + static sourceToDisplay(source) { + return source;//TODO + } + static serviceToDisplay(service) { + return service;//TODO + } + static methodToDisplay(method) { + return method;//TODO } @@ -177,8 +187,16 @@ export class ExtItemSearch extends PageUtility { return true; } + static errCount = 0; static async handleErrResult(result) { - //TODO + ExtItemSearch.errCount++; + + let newAlert = $(''); + + newAlert.append($('

      Service:

      ').text("Service: " + result.service)); + newAlert.append($('

      p>').text(result.displayMessage)); + + ExtItemSearch.searchResultsErrContent.append(newAlert); } static async handleNotFoundResult(result) { @@ -191,9 +209,16 @@ export class ExtItemSearch extends PageUtility { let resultRow = $(''); resultRow.append($('').text(ExtItemSearch.resultCount++)); - resultRow.append($('').text(result.service));//TODO:: better name - resultRow.append($('').text(result.source));//TODO:: better name - resultRow.append($('').text(result.method));//TODO:: better name + + let service = ExtItemSearch.serviceToDisplay(result.service); + + if(result.service === result.source) { + resultRow.append($('').text(service)); + }else { + resultRow.append($('').text(service)); + resultRow.append($('').text(ExtItemSearch.sourceToDisplay(result.source))); + } + resultRow.append($('').text(ExtItemSearch.methodToDisplay(result.method))); resultRow.append($('').text(result.images.length)); resultRow.append($('').text(Object.keys(result.prices).length)); resultRow.append($('').text(Object.keys(result.links).length)); @@ -214,70 +239,6 @@ export class ExtItemSearch extends PageUtility { resultMainBody.append(ExtItemSearch.createSearchResultSection("Name", result.unifiedName, ItemAddEdit.addEditItemNameInput)); resultMainBody.append(ExtItemSearch.createSearchResultSection("Description", result.description, ItemAddEdit.addEditItemDescriptionInput)); - if (result.images.length) { - let carouselId = "extSearchResultImgCarousel-" + ExtItemSearch.carouselNum++; - let imagesSection = $('
      Images:
      '); - - let carousel = $(''); - let carouselInner = carousel.find(".carousel-inner"); - - let imgPromises = []; - result.images.forEach(function (curImageLoc, i) { - let curPromise = async function () { - console.log("Getting image ", i); - - let imageData = await ExtItemSearch.getImageBase64FromUrl(curImageLoc); - - if (!imageData) { - console.error("FAILED to get image data for " + i + " - " + curImageLoc); - return; - } - let newCarImageDir = $( - ' \n' - ); - let newCarImage = newCarImageDir.find("img"); - newCarImage.prop("src", imageData); - - let useButton = $(''); - useButton.on("click", function () { - ExtItemSearch.addOrGetAndSelectImage(curImageLoc, result.unifiedName, imageData); - }); - newCarImageDir.find(".carousel-caption").append(useButton); - - carouselInner.append(newCarImageDir); - console.log("Finished getting image " + i); - } - - imgPromises.push(curPromise()); - }); - await Promise.all(imgPromises); - - $(carouselInner.children()[0]).addClass('active'); - - console.log("Finished getting " + carouselInner.children().length + " images"); - if (carouselInner.children().length) { - imagesSection.append(carousel); - imagesSection.append($('
      ')); - resultMainBody.append(imagesSection); - } - } if (Object.keys(result.prices).length) { let pricesSection = $('
      Prices:
      '); @@ -393,6 +354,71 @@ export class ExtItemSearch extends PageUtility { resultMainBody.append(attsSection); } + if (result.images.length) { + let carouselId = "extSearchResultImgCarousel-" + ExtItemSearch.carouselNum++; + let imagesSection = $('
      Images:
      '); + + let carousel = $(''); + let carouselInner = carousel.find(".carousel-inner"); + + let imgPromises = []; + result.images.forEach(function (curImageLoc, i) { + let curPromise = async function () { + console.log("Getting image ", i); + + let imageData = await ExtItemSearch.getImageBase64FromUrl(curImageLoc); + + if (!imageData) { + console.error("FAILED to get image data for " + i + " - " + curImageLoc); + return; + } + let newCarImageDir = $( + ' \n' + ); + let newCarImage = newCarImageDir.find("img"); + newCarImage.prop("src", imageData); + + let useButton = $(''); + useButton.on("click", function () { + ExtItemSearch.addOrGetAndSelectImage(curImageLoc, result.unifiedName, imageData); + }); + newCarImageDir.find(".carousel-caption").append(useButton); + + carouselInner.append(newCarImageDir); + console.log("Finished getting image " + i); + } + + imgPromises.push(curPromise()); + }); + await Promise.all(imgPromises); + + $(carouselInner.children()[0]).addClass('active'); + + console.log("Finished getting " + carouselInner.children().length + " images"); + if (carouselInner.children().length) { + imagesSection.append(carousel); + imagesSection.append($('
      ')); + resultMainBody.append(imagesSection); + } + } + resultViewRow.append(resultMainBody); resultViewRow.hide(); return resultViewRow; @@ -410,7 +436,7 @@ export class ExtItemSearch extends PageUtility { ExtItemSearch.extSearchResultsTableContent.append(resultViewRow); } - static async handleExtItemSearchResult(result) { + static handleExtItemSearchResult(result) { console.debug("Handling external item search result: ", result); switch (result.type) { case "SUCCESS": @@ -429,6 +455,7 @@ export class ExtItemSearch extends PageUtility { ExtItemSearch.extSearchResults.html("

      No Results!

      "); } + ExtItemSearch.errCount = 0; ExtItemSearch.resultCount = 1; let resultPromises = []; results.forEach( @@ -439,7 +466,13 @@ export class ExtItemSearch extends PageUtility { await Promise.all(resultPromises); ExtItemSearch.searchResultsCount.text(ExtItemSearch.resultCount); + ExtItemSearch.searchResultsErrCount.text(ExtItemSearch.errCount); ExtItemSearch.extSearchResultsContainer.show(); + + if(ExtItemSearch.errCount) { + ExtItemSearch.searchResultsErrTab.prop("disabled", false); + } + console.log("Finished processing ext item search results."); } diff --git a/software/core/oqm-core-base-station/src/main/resources/templates/tags/itemAddEditModal.html b/software/core/oqm-core-base-station/src/main/resources/templates/tags/itemAddEditModal.html index 3fac3e32a5..4125643f0e 100644 --- a/software/core/oqm-core-base-station/src/main/resources/templates/tags/itemAddEditModal.html +++ b/software/core/oqm-core-base-station/src/main/resources/templates/tags/itemAddEditModal.html @@ -25,19 +25,19 @@
      -
      - - -
      Specify none to use "any".
      -
      - + {!
      !} + {!!} + {!!} + {!
      Specify none to use "any".
      !} + {!
      !}
      - + {#for curMethod in extSearchMethods.properties()} + + {/for}
      diff --git a/software/core/oqm-core-base-station/src/main/resources/templates/webui/pages/items.html b/software/core/oqm-core-base-station/src/main/resources/templates/webui/pages/items.html index 170da811ca..ed98e07b7e 100644 --- a/software/core/oqm-core-base-station/src/main/resources/templates/webui/pages/items.html +++ b/software/core/oqm-core-base-station/src/main/resources/templates/webui/pages/items.html @@ -55,7 +55,7 @@

      {/pageContent} {#modals} {#objView/itemViewModal rootPrefix=rootPrefix /} - {#itemAddEditModal allUnitMap=allUnitMap allCategorySearchResults=allCategorySearchResults rootPrefix=rootPrefix /} + {#itemAddEditModal allUnitMap=allUnitMap allCategorySearchResults=allCategorySearchResults rootPrefix=rootPrefix extSearchMethods=extSearchMethods /} {#modal id='addItemFromFile' size='xl' title='Add item(s) from file' submitForm='addItemFromFileForm' submitDismiss=false} {#titleIcon}{#icons/addFile}{/icons/addFile}{/titleIcon}
      From 05b718959705f92bd8cd2b561d55ce506cc5dab4 Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Sun, 26 Apr 2026 00:16:10 -0400 Subject: [PATCH 26/38] Plugin - Ext Item Search - Polish and added endpoints for available methods --- .../interfaces/ItemLookupRestInterface.java | 38 ++++++++++--------- .../service/ExtItemLookupService.java | 21 ++++++++++ 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java index 1d33dce6fc..034a110ad2 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/interfaces/ItemLookupRestInterface.java @@ -22,8 +22,11 @@ import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemSearch; import tech.ebp.oqm.plugin.extItemSearch.model.lookupResult.LookupResult; import tech.ebp.oqm.plugin.extItemSearch.service.ExtItemLookupService; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupMethod; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupService; import java.util.List; +import java.util.Map; @Slf4j @Path("/api/v1") @@ -35,20 +38,13 @@ public class ItemLookupRestInterface { ExtItemLookupService productLookupService; @GET - @Path("/providers") + @Path("/info/providers") @Operation( summary = "Gets all supported providers." ) @APIResponse( responseCode = "200", - description = "Image retrieved.", - content = @Content( - mediaType = MediaType.APPLICATION_JSON, - schema = @Schema( - type = SchemaType.ARRAY, - implementation = ExtItemLookupProviderInfo.class - ) - ) + description = "Image retrieved." ) @PermitAll @Produces(MediaType.APPLICATION_JSON) @@ -56,6 +52,21 @@ public List allProviderInfo() { return this.productLookupService.getProductProviderInfo(); } + @GET + @Path("/info/methods") + @Operation( + summary = "Gets all search methods currently enabled." + ) + @APIResponse( + responseCode = "200", + description = "Image retrieved." + ) + @PermitAll + @Produces(MediaType.APPLICATION_JSON) + public Map> availableMethods() { + return this.productLookupService.getAvailableSearchMethods(); + } + @GET @Path("/search") @Operation( @@ -63,14 +74,7 @@ public List allProviderInfo() { ) @APIResponse( responseCode = "200", - description = "Image retrieved.", - content = @Content( - mediaType = MediaType.APPLICATION_JSON, - schema = @Schema( - type = SchemaType.ARRAY, - implementation = ExtItemLookupProviderInfo.class - ) - ) + description = "Image retrieved." ) @PermitAll @Produces(MediaType.APPLICATION_JSON) diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/ExtItemLookupService.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/ExtItemLookupService.java index 42357eb6e3..5962c61052 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/ExtItemLookupService.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/ExtItemLookupService.java @@ -1,5 +1,6 @@ package tech.ebp.oqm.plugin.extItemSearch.service; +import io.quarkus.cache.CacheResult; import io.smallrye.mutiny.Multi; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; @@ -14,6 +15,9 @@ import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.providers.rebrickable.RebrickableService; import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.providers.upcItemDb.UpcItemDbService; import tech.ebp.oqm.plugin.extItemSearch.model.ExtItemLookupProviderInfo; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupMethod; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupService; +import tech.ebp.oqm.plugin.extItemSearch.service.extItemSearchService.utils.LookupSource; import java.util.*; @@ -51,10 +55,27 @@ public Set getSearchServices() { return this.searchServices; } + @CacheResult(cacheName = "productProviderInfo") public List getProductProviderInfo() { return servicesToInfoList(this.searchServices); } + @CacheResult(cacheName = "availableSearchMethods") + public Map> getAvailableSearchMethods() { + Map> output = new LinkedHashMap<>(); + + for (ItemSearchService curService : this.searchServices) { + if(!curService.isEnabled()){ + continue; + } + for (LookupMethod curMethod : curService.getProviderInfo().getLookupMethods()) { + output.computeIfAbsent(curMethod, k->new ArrayList<>()) + .add(curService.getService()); + } + } + + return output; + } public Multi search(ExtItemSearch search) { From 19b01b277c84d2b4423d06f4b92fc776718656f8 Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Sun, 26 Apr 2026 00:24:47 -0400 Subject: [PATCH 27/38] Plugin - Ext Item Search - metrics tweaks --- .../src/main/resources/application.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/software/plugins/external-item-search/src/main/resources/application.yml b/software/plugins/external-item-search/src/main/resources/application.yml index 43ab4279ff..78344548e5 100644 --- a/software/plugins/external-item-search/src/main/resources/application.yml +++ b/software/plugins/external-item-search/src/main/resources/application.yml @@ -63,6 +63,13 @@ quarkus: health: openapi: included: true + otel: + metrics: + enabled: true + traces: + enabled: true + logs: + enabled: true wiremock: devservices: enabled: true From 0cb9ee611cd630d48e5d14043e17dead7fa2bbb8 Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Sun, 26 Apr 2026 00:37:07 -0400 Subject: [PATCH 28/38] Core - Base Station - Fixed overview page --- .../interfaces/ui/pages/OverviewUi.java | 17 +++++++++++++++-- .../templates/webui/pages/overview.html | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/software/core/oqm-core-base-station/src/main/java/tech/ebp/oqm/core/baseStation/interfaces/ui/pages/OverviewUi.java b/software/core/oqm-core-base-station/src/main/java/tech/ebp/oqm/core/baseStation/interfaces/ui/pages/OverviewUi.java index ef4d8aa5c5..0aa232b806 100644 --- a/software/core/oqm-core-base-station/src/main/java/tech/ebp/oqm/core/baseStation/interfaces/ui/pages/OverviewUi.java +++ b/software/core/oqm-core-base-station/src/main/java/tech/ebp/oqm/core/baseStation/interfaces/ui/pages/OverviewUi.java @@ -2,6 +2,7 @@ import io.quarkus.qute.Location; import io.quarkus.qute.Template; +import io.quarkus.rest.client.reactive.QuarkusRestClientBuilder; import io.smallrye.mutiny.Uni; import jakarta.annotation.security.RolesAllowed; import jakarta.enterprise.context.RequestScoped; @@ -18,6 +19,7 @@ import org.eclipse.microprofile.openapi.annotations.tags.Tag; import org.eclipse.microprofile.openapi.annotations.tags.Tags; import org.eclipse.microprofile.rest.client.inject.RestClient; +import tech.ebp.oqm.core.baseStation.service.ExternalItemSearchClient; import tech.ebp.oqm.core.baseStation.service.modelTweak.SearchResultTweak; import tech.ebp.oqm.core.baseStation.utils.Roles; import tech.ebp.oqm.core.baseStation.utils.Searches; @@ -27,6 +29,8 @@ import tech.ebp.oqm.lib.core.api.quarkus.runtime.restClient.searchObjects.StorageBlockSearch; import tech.ebp.oqm.lib.core.api.quarkus.runtime.restClient.searchObjects.StoredSearch; +import java.net.MalformedURLException; +import java.net.URL; import java.util.Map; import java.util.Optional; import java.util.concurrent.ExecutorService; @@ -63,6 +67,9 @@ public class OverviewUi extends UiProvider { @Inject SearchResultTweak searchResultTweak; + @ConfigProperty(name = "quarkus.rest-client.externalItemSearch.url", defaultValue = " ") + String extSearchUrl; + @GET @Path("overview") @RolesAllowed(Roles.INVENTORY_VIEW) @@ -71,7 +78,8 @@ public Uni overview( @QueryParam("lowStockPage") Optional lowStockPage, @QueryParam("expiringPage") Optional expiringPage, @QueryParam("expiredPage") Optional expiredPage - ) { + ) throws MalformedURLException { + boolean extSearchEnabled = !this.extSearchUrl.isBlank(); StorageBlockSearch treeBlockSearch = Searches.BLOCK_PARENT_SEARCH.toBuilder() .pageSize(this.getStorageTreeDefaultPageSize()) .pageNum(storageBlockPage.orElse(1)) @@ -103,7 +111,12 @@ public Uni overview( "lowStockResults", this.coreApiClient.invItemSearch(this.getBearerHeaderStr(), this.getSelectedDb(), lowStockSearch), "currency", this.coreApiClient.getCurrency(this.getBearerHeaderStr()), "allUnitMap", this.coreApiClient.unitGetAll(this.getBearerHeaderStr()), - "allCategorySearchResults", this.coreApiClient.itemCatSearch(this.getBearerHeaderStr(), this.getSelectedDb(), new ItemCategorySearch()) + "allCategorySearchResults", this.coreApiClient.itemCatSearch(this.getBearerHeaderStr(), this.getSelectedDb(), new ItemCategorySearch()), + "extSearchMethods", extSearchEnabled ? + QuarkusRestClientBuilder.newBuilder() + .baseUrl(new URL(this.extSearchUrl)) + .build(ExternalItemSearchClient.class).allMethodInfo() + : Uni.createFrom().nullItem() ) ); } diff --git a/software/core/oqm-core-base-station/src/main/resources/templates/webui/pages/overview.html b/software/core/oqm-core-base-station/src/main/resources/templates/webui/pages/overview.html index 27f40a2f29..34fd8e2be0 100644 --- a/software/core/oqm-core-base-station/src/main/resources/templates/webui/pages/overview.html +++ b/software/core/oqm-core-base-station/src/main/resources/templates/webui/pages/overview.html @@ -144,7 +144,7 @@

      {/footerButtons} {/objView/itemViewModal} - {#itemAddEditModal allUnitMap=allUnitMap allCategorySearchResults=allCategorySearchResults currency=currency rootPrefix=rootPrefix /} + {#itemAddEditModal allUnitMap=allUnitMap allCategorySearchResults=allCategorySearchResults currency=currency rootPrefix=rootPrefix extSearchMethods=extSearchMethods /} {#search/storage/searchSelectModal otherModalId="addEditItemModal" rootPrefix=rootPrefix /} {#search/image/imageSearchSelectModal otherModalId="addEditItemModal" rootPrefix=rootPrefix /} {#search/item/itemSearchSelectModal rootPrefix=rootPrefix allCategorySearchResults=allCategorySearchResults /} From c096f7135f934a2d692755a255677485470a19a7 Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Sun, 26 Apr 2026 01:16:53 -0400 Subject: [PATCH 29/38] Core - Base Station - tweak from review --- .../META-INF/resources/res/js/item/ExtItemSearch.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js b/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js index 540ff26b81..a88574372f 100644 --- a/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js +++ b/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js @@ -194,7 +194,7 @@ export class ExtItemSearch extends PageUtility { let newAlert = $(''); newAlert.append($('

      Service:

      ').text("Service: " + result.service)); - newAlert.append($('

      p>').text(result.displayMessage)); + newAlert.append($('

      ').text(result.displayMessage)); ExtItemSearch.searchResultsErrContent.append(newAlert); } @@ -317,6 +317,10 @@ export class ExtItemSearch extends PageUtility { }); curAtt.append(useButt); + curAtt.append($('') + .html(Icons.link) + .attr("href", val) + ); linkList.append(curAtt); }); @@ -382,6 +386,7 @@ export class ExtItemSearch extends PageUtility { if (!imageData) { console.error("FAILED to get image data for " + i + " - " + curImageLoc); + //TODO:: add to list of failed image URL's return; } let newCarImageDir = $( @@ -452,7 +457,7 @@ export class ExtItemSearch extends PageUtility { console.log("Got Results! # results: " + results.length); if (results.length === 0) { - ExtItemSearch.extSearchResults.html("

      No Results!

      "); + ExtItemSearch.extSearchResultsTableContent.html("No Results!"); } ExtItemSearch.errCount = 0; From e5952f9fd2642a74dd1361aaf0b7c28888f0352f Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Sun, 26 Apr 2026 01:18:24 -0400 Subject: [PATCH 30/38] SNH - Station Captain - fix secret setting in config manager. Fixed config setting file precedence when reading in --- .../Single Host/Station-Captain/src/lib/ConfigManager.py | 2 +- deployment/Single Host/Station-Captain/src/oqm-config.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/deployment/Single Host/Station-Captain/src/lib/ConfigManager.py b/deployment/Single Host/Station-Captain/src/lib/ConfigManager.py index bdfc927999..774deb14a8 100644 --- a/deployment/Single Host/Station-Captain/src/lib/ConfigManager.py +++ b/deployment/Single Host/Station-Captain/src/lib/ConfigManager.py @@ -230,7 +230,7 @@ def __init__( def rereadConfigData(self): self.configData = self.readFile(self.mainConfigFile) - for file in os.listdir(self.additionalConfigsDir): + for file in sorted(os.listdir(self.additionalConfigsDir)): if file.endswith(".json"): curUpdates = self.readFile(self.additionalConfigsDir + "/" + file) self.configData = ConfigManager.mergeDicts(self.configData, curUpdates) diff --git a/deployment/Single Host/Station-Captain/src/oqm-config.py b/deployment/Single Host/Station-Captain/src/oqm-config.py index 006ac80cbe..b15aa87626 100755 --- a/deployment/Single Host/Station-Captain/src/oqm-config.py +++ b/deployment/Single Host/Station-Captain/src/oqm-config.py @@ -138,9 +138,9 @@ def set(args): if secret: json = mainCM.setSecretValInFile( - configKeyToSet=args.setSecret[0], - configValToSet=args.setSecret[1], - configFile=args.setSecret[2] + configKeyToSet=secret[0], + configValToSet=secret[1], + configFile=secret[2] ) else: json = mainCM.setConfigValInFile( From ec4847494a0190caf84fd9a2aed0598847ee4f29 Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Sun, 26 Apr 2026 17:02:31 -0400 Subject: [PATCH 31/38] Core - Base Station - added failed image list --- .../resources/res/js/item/ExtItemSearch.js | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js b/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js index a88574372f..56108ea963 100644 --- a/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js +++ b/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js @@ -377,6 +377,7 @@ export class ExtItemSearch extends PageUtility { '

      '); let carouselInner = carousel.find(".carousel-inner"); + let failedImages = []; let imgPromises = []; result.images.forEach(function (curImageLoc, i) { let curPromise = async function () { @@ -386,7 +387,7 @@ export class ExtItemSearch extends PageUtility { if (!imageData) { console.error("FAILED to get image data for " + i + " - " + curImageLoc); - //TODO:: add to list of failed image URL's + failedImages.push(curImageLoc); return; } let newCarImageDir = $( @@ -417,8 +418,25 @@ export class ExtItemSearch extends PageUtility { $(carouselInner.children()[0]).addClass('active'); console.log("Finished getting " + carouselInner.children().length + " images"); + let hadContent = false; if (carouselInner.children().length) { + hadContent=true; imagesSection.append(carousel); + } + if(failedImages.length){ + hadContent=true; + imagesSection.append($("

      Failed to load images:

      ")); + let failedImgList = $('
        '); + failedImages.forEach(function(curFailedImg){ + failedImgList.append( + $('') + .text(curFailedImg) + .attr("href", curFailedImg) + ); + }); + imagesSection.append(failedImgList); + } + if(hadContent){ imagesSection.append($('
        ')); resultMainBody.append(imagesSection); } From 805992d47857f67bc47311994a9da78a8ab74425 Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Mon, 27 Apr 2026 23:52:17 -0400 Subject: [PATCH 32/38] Core - Base Station - fixed associated links functionality --- .../resources/META-INF/resources/res/js/AssociatedLinks.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/AssociatedLinks.js b/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/AssociatedLinks.js index 67f902c974..3a6b3bc43f 100644 --- a/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/AssociatedLinks.js +++ b/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/AssociatedLinks.js @@ -2,8 +2,9 @@ import {SelectedObjectDivUtils} from "./SelectedObjectDivUtils.js"; import {Links} from "./Links.js"; import {Icons} from "./Icons.js"; import {PageComponents} from "./PageComponents.js"; +import {PageUtility} from "./utilClasses/PageUtility.js"; -export class AssociatedLinks { +export class AssociatedLinks extends PageUtility { static Form = class { static Input = class { static getInput(innerElem){ @@ -170,4 +171,8 @@ export class AssociatedLinks { return output; } } + + static { + window.AssociatedLinks = this; + } } From 8b38b9f198b1a417e261b2ddc011420f0c62f710 Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Mon, 27 Apr 2026 23:52:59 -0400 Subject: [PATCH 33/38] Core - Base Station - Polish of ext item search results --- .../resources/res/js/item/ExtItemSearch.js | 68 ++++++++++++------- .../templates/tags/itemAddEditModal.html | 4 +- 2 files changed, 44 insertions(+), 28 deletions(-) diff --git a/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js b/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js index 56108ea963..ce4d3577a1 100644 --- a/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js +++ b/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js @@ -36,9 +36,11 @@ export class ExtItemSearch extends PageUtility { static sourceToDisplay(source) { return source;//TODO } + static serviceToDisplay(service) { return service;//TODO } + static methodToDisplay(method) { return method;//TODO } @@ -188,6 +190,7 @@ export class ExtItemSearch extends PageUtility { } static errCount = 0; + static async handleErrResult(result) { ExtItemSearch.errCount++; @@ -208,17 +211,28 @@ export class ExtItemSearch extends PageUtility { static getItemResultRow(result) { let resultRow = $(''); - resultRow.append($('').text(ExtItemSearch.resultCount++)); + resultRow.append($('').text(++ExtItemSearch.resultCount)); + let service = ExtItemSearch.serviceToDisplay(result.service); - if(result.service === result.source) { - resultRow.append($('').text(service)); - }else { - resultRow.append($('').text(service)); - resultRow.append($('').text(ExtItemSearch.sourceToDisplay(result.source))); - } - resultRow.append($('').text(ExtItemSearch.methodToDisplay(result.method))); + resultRow.append($('') + .append($('').text(result.name)) + .append($('
        ')) + .append( + $('') + .append($('').text( + result.service === result.source ? + service : + service + " (" + ExtItemSearch.sourceToDisplay(result.source) + ")" + ) + ) + .append(' / ') + .append($('').text(ExtItemSearch.methodToDisplay(result.method))) + ) + + ); + resultRow.append($('').text(result.images.length)); resultRow.append($('').text(Object.keys(result.prices).length)); resultRow.append($('').text(Object.keys(result.links).length)); @@ -234,7 +248,7 @@ export class ExtItemSearch extends PageUtility { //TODO:: promise for waiting on image load... need to diagnose this let resultViewRow = $(''); - let resultMainBody = $(''); + let resultMainBody = $(''); resultMainBody.append(ExtItemSearch.createSearchResultSection("Name", result.unifiedName, ItemAddEdit.addEditItemNameInput)); resultMainBody.append(ExtItemSearch.createSearchResultSection("Description", result.description, ItemAddEdit.addEditItemDescriptionInput)); @@ -317,9 +331,10 @@ export class ExtItemSearch extends PageUtility { }); curAtt.append(useButt); - curAtt.append($('') - .html(Icons.link) - .attr("href", val) + curAtt.append( + $('') + .html(Icons.link) + .attr("href", val) ); linkList.append(curAtt); @@ -420,23 +435,25 @@ export class ExtItemSearch extends PageUtility { console.log("Finished getting " + carouselInner.children().length + " images"); let hadContent = false; if (carouselInner.children().length) { - hadContent=true; + hadContent = true; imagesSection.append(carousel); } - if(failedImages.length){ - hadContent=true; + if (failedImages.length) { + hadContent = true; imagesSection.append($("

        Failed to load images:

        ")); let failedImgList = $('
          '); - failedImages.forEach(function(curFailedImg){ + failedImages.forEach(function (curFailedImg) { failedImgList.append( - $('') - .text(curFailedImg) - .attr("href", curFailedImg) + $("
        • ").append( + $('') + .text(curFailedImg) + .attr("href", curFailedImg) + ) ); }); imagesSection.append(failedImgList); } - if(hadContent){ + if (hadContent) { imagesSection.append($('
          ')); resultMainBody.append(imagesSection); } @@ -474,12 +491,9 @@ export class ExtItemSearch extends PageUtility { static async handleExtItemSearchResults(results) { console.log("Got Results! # results: " + results.length); - if (results.length === 0) { - ExtItemSearch.extSearchResultsTableContent.html("No Results!"); - } ExtItemSearch.errCount = 0; - ExtItemSearch.resultCount = 1; + ExtItemSearch.resultCount = 0; let resultPromises = []; results.forEach( function (result) { @@ -487,12 +501,16 @@ export class ExtItemSearch extends PageUtility { } ); + if (ExtItemSearch.resultCount === 0) { + ExtItemSearch.extSearchResultsTableContent.html("No Results!"); + } + await Promise.all(resultPromises); ExtItemSearch.searchResultsCount.text(ExtItemSearch.resultCount); ExtItemSearch.searchResultsErrCount.text(ExtItemSearch.errCount); ExtItemSearch.extSearchResultsContainer.show(); - if(ExtItemSearch.errCount) { + if (ExtItemSearch.errCount) { ExtItemSearch.searchResultsErrTab.prop("disabled", false); } diff --git a/software/core/oqm-core-base-station/src/main/resources/templates/tags/itemAddEditModal.html b/software/core/oqm-core-base-station/src/main/resources/templates/tags/itemAddEditModal.html index 4125643f0e..0dc5ba7226 100644 --- a/software/core/oqm-core-base-station/src/main/resources/templates/tags/itemAddEditModal.html +++ b/software/core/oqm-core-base-station/src/main/resources/templates/tags/itemAddEditModal.html @@ -69,9 +69,7 @@ # - Service - Source - Method + Name / Service / Source / Method {#icons/images /} {#icons/pricing /} {#icons/link /} From 1832233ec2e82b5374a2883bac52964145b6b439 Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Tue, 28 Apr 2026 00:15:04 -0400 Subject: [PATCH 34/38] Core - API - quarkus version bump --- software/core/oqm-core-api/gradle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/software/core/oqm-core-api/gradle.properties b/software/core/oqm-core-api/gradle.properties index a9f4bca438..c360cd3471 100644 --- a/software/core/oqm-core-api/gradle.properties +++ b/software/core/oqm-core-api/gradle.properties @@ -1,7 +1,7 @@ #Gradle properties quarkusPluginId=io.quarkus -quarkusPluginVersion=3.34.2 -quarkusPlatformVersion=3.34.2 +quarkusPluginVersion=3.34.6 +quarkusPlatformVersion=3.34.6 quarkusPlatformGroupId=io.quarkus quarkusPlatformArtifactId=quarkus-universe-bom org.gradle.logging.level=INFO From cd126807b5684c6ec5aaf468daf3cfa4ab08a7c6 Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Tue, 28 Apr 2026 00:22:57 -0400 Subject: [PATCH 35/38] Plugin - Ext Item Search - quarkus version bump --- software/plugins/external-item-search/gradle.properties | 4 ++-- .../service/extItemSearchService/utils/LookupMethod.java | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/software/plugins/external-item-search/gradle.properties b/software/plugins/external-item-search/gradle.properties index 0bd2225299..b5cf2ea6c3 100644 --- a/software/plugins/external-item-search/gradle.properties +++ b/software/plugins/external-item-search/gradle.properties @@ -1,6 +1,6 @@ #Gradle properties quarkusPluginId=io.quarkus -quarkusPluginVersion=3.34.2 -quarkusPlatformVersion=3.34.2 +quarkusPluginVersion=3.34.6 +quarkusPlatformVersion=3.34.6 quarkusPlatformGroupId=io.quarkus.platform quarkusPlatformArtifactId=quarkus-bom \ No newline at end of file diff --git a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/utils/LookupMethod.java b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/utils/LookupMethod.java index 420897e09f..1e980e025d 100644 --- a/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/utils/LookupMethod.java +++ b/software/plugins/external-item-search/src/main/java/tech/ebp/oqm/plugin/extItemSearch/service/extItemSearchService/utils/LookupMethod.java @@ -6,5 +6,4 @@ public enum LookupMethod { PART_NUM, SET_NUM, WEBPAGE; - } From 55126d47d444a38497c81372ab6f2ba79579a6f7 Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Tue, 28 Apr 2026 00:32:05 -0400 Subject: [PATCH 36/38] Core - Base Station - Bugfix, quarkus version bump --- software/core/oqm-core-base-station/gradle.properties | 4 ++-- .../META-INF/resources/res/js/obj/item/ItemAddEdit.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/software/core/oqm-core-base-station/gradle.properties b/software/core/oqm-core-base-station/gradle.properties index 3b7495c3d2..b5cf2ea6c3 100644 --- a/software/core/oqm-core-base-station/gradle.properties +++ b/software/core/oqm-core-base-station/gradle.properties @@ -1,6 +1,6 @@ #Gradle properties quarkusPluginId=io.quarkus -quarkusPluginVersion=3.32.4 -quarkusPlatformVersion=3.32.4 +quarkusPluginVersion=3.34.6 +quarkusPlatformVersion=3.34.6 quarkusPlatformGroupId=io.quarkus.platform quarkusPlatformArtifactId=quarkus-bom \ No newline at end of file diff --git a/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/obj/item/ItemAddEdit.js b/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/obj/item/ItemAddEdit.js index 11e7880fdb..9c8a8ba9ad 100644 --- a/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/obj/item/ItemAddEdit.js +++ b/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/obj/item/ItemAddEdit.js @@ -99,7 +99,7 @@ export class ItemAddEdit extends PageUtility { ItemAddEdit.addEditItemFormMode.val(""); ItemAddEdit.addEditItemNameInput.val(""); ItemAddEdit.addEditItemDescriptionInput.setValue(""); - // Identifiers.reset(ItemAddEdit.identifierInputContainer); + Identifiers.reset(ItemAddEdit.identifierInputContainer); IdGeneratorSearchSelect.AssociatedInput.resetAssociatedIdGenListData(ItemAddEdit.associatedGeneratorInput); ItemAddEdit.addEditItemModalLabel.text("Item"); ItemAddEdit.addEditItemExpiryWarningThresholdInput.val(0); From 078009807c2cdf2b823291e3464c1ccabf223eb4 Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Tue, 28 Apr 2026 00:34:37 -0400 Subject: [PATCH 37/38] SNH - Station Captain - fix to the fix of secret setting in config manager --- deployment/Single Host/Station-Captain/src/oqm-config.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deployment/Single Host/Station-Captain/src/oqm-config.py b/deployment/Single Host/Station-Captain/src/oqm-config.py index b15aa87626..63c0df9e6d 100755 --- a/deployment/Single Host/Station-Captain/src/oqm-config.py +++ b/deployment/Single Host/Station-Captain/src/oqm-config.py @@ -138,9 +138,9 @@ def set(args): if secret: json = mainCM.setSecretValInFile( - configKeyToSet=secret[0], - configValToSet=secret[1], - configFile=secret[2] + configKeyToSet=configKeyToSet, + configValToSet=configValToSet, + configFile=configFile ) else: json = mainCM.setConfigValInFile( From 425031eb9ecdd3c6a36ed56c353210596f9ba58c Mon Sep 17 00:00:00 2001 From: GregJohnStewart Date: Tue, 28 Apr 2026 00:37:07 -0400 Subject: [PATCH 38/38] Core - Base Station - minor fix from review --- .../resources/META-INF/resources/res/js/item/ExtItemSearch.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js b/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js index ce4d3577a1..f55d2ee8b6 100644 --- a/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js +++ b/software/core/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/item/ExtItemSearch.js @@ -500,12 +500,12 @@ export class ExtItemSearch extends PageUtility { resultPromises.push(ExtItemSearch.handleExtItemSearchResult(result)); } ); + await Promise.all(resultPromises); if (ExtItemSearch.resultCount === 0) { ExtItemSearch.extSearchResultsTableContent.html("No Results!"); } - await Promise.all(resultPromises); ExtItemSearch.searchResultsCount.text(ExtItemSearch.resultCount); ExtItemSearch.searchResultsErrCount.text(ExtItemSearch.errCount); ExtItemSearch.extSearchResultsContainer.show();