package com.vaessl.app.homebox; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Component; import org.springframework.web.client.RestClient; import com.vaessl.app.connection.ConnectionEntity; import com.vaessl.app.connection.ConnectionIdentifiable; import com.vaessl.app.connection.ConnectionRepository; import com.vaessl.app.connection.HomeboxConnectionEntity; import com.vaessl.app.exception.ConnectionNotFoundException; import com.vaessl.app.exception.RemoteApiException; import com.vaessl.app.shared.ServiceItem; import static com.vaessl.app.shared.Endpoint.HOMEBOX_QUERY_ALL_ITEMS; /** * Fetches items from the Homebox entities API. Shared by {@code HomeboxSearchProvider} and * {@code HomeboxSyncProvider} so both keyword search and vector-store sync page through the same * remote call and item mapping. */ @Component public class HomeboxItemClient { private final RestClient.Builder restClientBuilder; private final ConnectionRepository cRepository; public HomeboxItemClient(RestClient.Builder restClienBuilder, ConnectionRepository cRepository) { this.restClientBuilder = restClienBuilder; this.cRepository = cRepository; } /** * Fetches one page of items from Homebox for the given connection. * * @param connection identifies which stored connection (app URL, username) to query * @param query optional keyword filter; {@code null} returns all items for the page * @param pageable page number and size to request * @return the mapped page of items along with the resolved connection ID * @throws com.vaessl.app.exception.ConnectionNotFoundException if no matching connection is * stored * @throws com.vaessl.app.exception.RemoteApiException if Homebox returns an empty response body */ public HomeboxItemPage hbResponse(ConnectionIdentifiable connection, String query, Pageable pageable) { ConnectionEntity entity = cRepository.findByAppUrlAndUsername(connection.appUrl(), connection.username()); if (!(entity instanceof HomeboxConnectionEntity hbEntity)) { throw new ConnectionNotFoundException(); } HomeboxItemsResponse response = restClientBuilder.baseUrl(connection.appUrl()).build().get() .uri(u -> u.path(HOMEBOX_QUERY_ALL_ITEMS.getValue()).queryParam("q", query) .queryParam("page", pageable.getPageNumber() + 1) .queryParam("pageSize", pageable.getPageSize()).build()) .headers(h -> h.setBearerAuth(hbEntity.getToken())).retrieve() .body(HomeboxItemsResponse.class); if (response == null) { throw new RemoteApiException(connection.appUrl(), HOMEBOX_QUERY_ALL_ITEMS.getValue()); } List items = response.items().stream().map(i -> { String id = i.id(); String title = i.name(); String description = i.description(); HomeboxParent parent = i.parent(); Map> extraData = new LinkedHashMap<>(); Map locationData = new LinkedHashMap<>(); if (parent.name() != null && !parent.name().isBlank()) { locationData.put("name", parent.name()); } if (parent.description() != null && !parent.description().isBlank()) { locationData.put("description", parent.description()); } if (locationData != null && !locationData.isEmpty()) { extraData.put("location", locationData); } return new ServiceItem(id, title, description, extraData); }).toList(); return new HomeboxItemPage(new PageImpl<>(items, pageable, response.total()), hbEntity.getId()); } public record HomeboxItemPage(Page page, Long connectionId) { } private record HomeboxItemsResponse(int page, int pageSize, int total, List items) { } private record HomeboxItem(String id, String name, String description, HomeboxParent parent) { } private record HomeboxParent(String name, String description) { } }