Homebox's location fields were flattened into single-purpose keys (locationName/locationDescription), which gave EmbeddingService no way to group related fields when rendering vector-store content. extraData is now Map<String, Map<String, Object>>, letting HomeboxItemClient express "location" as its own section; buildExtraData renders each section as a header with indented fields instead of a flat "Extradata: key: value" line.
113 lines
4.4 KiB
Java
113 lines
4.4 KiB
Java
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<ServiceItem> items = response.items().stream().map(i -> {
|
|
String id = i.id();
|
|
String title = i.name();
|
|
String description = i.description();
|
|
|
|
HomeboxParent parent = i.parent();
|
|
Map<String, Map<String, Object>> extraData = new LinkedHashMap<>();
|
|
Map<String, Object> 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<ServiceItem> page, Long connectionId) {
|
|
}
|
|
|
|
private record HomeboxItemsResponse(int page, int pageSize, int total,
|
|
List<HomeboxItem> items) {
|
|
}
|
|
|
|
private record HomeboxItem(String id, String name, String description, HomeboxParent parent) {
|
|
}
|
|
|
|
private record HomeboxParent(String name, String description) {
|
|
}
|
|
|
|
|
|
}
|