Linking data between two json responses.

I am completely banging my head against my desk trying to figure out the logic for this.
This goes beyond just vaadin - if this isnt allowed please do let me know and I will remove my post.
Firstly, let me go over my goal, then how I am currently trying to achieve that, and finally the issues I’m running into.

Goal:
I want to use two different endpoints that share a common datapoint in their responses. Using that datapoint, I would match the data together. For instance the first response returns a list of serial numbers, and their associated order number. The second response returns an order number, and a bunch of data related to that order number, such as billing address. Clearly the linking datapoint in these responses is the order_number field.

Lets call the first response /get_scanned_serials, and the second response /order/get. While /order/get will have a lot of data in the response, I am only interested in using the order_number field to get the customer’s name for each corresponding serial number that have been sold. This data would be displayed in a grid with columns of Serial numbers, order number, and the customer name.

How am I currently trying to achieve that?
I have two endpoints I am calling. These endpoints are limited in the parameters I can use.
/get_scanned_serials is limited to a start date and end date, no pagination supported for this endpoint. Currently, the parameter start is the date before our first order, and end is the current day. This will return thousands of serials in one response. No limit is applied to this endpoint. The response will eventually be cached so we are not creating such a large get request each time.
/order/get is limited to pagination(and must use it). Limit is 100 requests at a time. Parameters are limit and skip.

I have successfully pulled the data I want from my /get_scanned_serials endpoint and displayed it on the grid. Again this displays the serial, and order number.
I have tried to create a DTO class, with very little knowledge on how to both link these responses together, and only get the information I want from /order/get (corresponding customer name).

Issues I’m running into
The error I currently get using my DTO, and displaying that on my view is:
There was an exception while trying to navigate to 'searchbydate' with the root cause 'java.lang.IllegalArgumentException: Not enough variable values available to expand '"order_number"''

My second endpoint relies on the order number of the first endpoint. Here are those methods in my DataService class:

public SerialRoot getSerial() {
        return webClient
                .get()
                .uri("/order/get_scanned_serial_numbers?start=2021-11-29T00:00:00Z&end=%s".formatted(todaysDate))
                .header(auth, skuT + token)
                .retrieve()
                .bodyToMono(SerialRoot.class)
                .block();
    }

    public OrderRoot getOrder(String orderNumber, int skip) {
        return webClient
                .get()
                .uri(uriBuilder -> uriBuilder
                        .path("/order/get")
                        .queryParam("selector", "{\"order_number\":\"" + orderNumber + "\"}")
                        .queryParam("limit", 1)
                        .queryParam("skip", skip)
                        .build())
                .header(auth, skuT + token)
                .retrieve()
                .bodyToMono(OrderRoot.class)
                .block();
    }```

In my view, I get the order number of the response, and pass that into my `getOrder` method. 
```java
dtoGrid = new Grid<>(DTO.class, false);
        SerialRoot serialRoot = service.getSerial();
        List<SerialNumbers> serialNumbers = serialRoot.getSerial_numbers();
        List<DTO> dtoList = new ArrayList<>();
        for(SerialNumbers serialNumber : serialNumbers) {
            int skip = 0;
            while(true) {
                OrderRoot orderRoot = service.getOrder(serialNumber.getOrder_number(), skip);
                if(orderRoot == null) {
                    break;
                } else if(orderRoot.getOrder_number() == serialNumber.getOrder_number()) {
                    dto.setName(orderRoot.getStash().getShipping_information().getName());
                    dtoList.add(new DTO(serialRoot, orderRoot));
                    skip +=1;
                }
            }
        }
        dtoGrid.setItems(dtoList);
        return dtoGrid;
    }```

I am completely unsure on where to go with this, and any help would be greatly appreciated. If you need any other information let me know.

Can you please post the whole exception stack trace? It will help to identify the root cause of the exception

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.example.application.views.SearchByDateView': Bean instantiation via constructor failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.example.application.views.SearchByDateView]: Constructor threw exception; nested exception is java.lang.IllegalArgumentException: Not enough variable values available to expand '"order_number"'

Is this all you have in the logs? It is still missing the root cause

Anyway, the error is in the constructor of the view class. Can you share the whole class source code?

https://pastebin.com/XR6qMpg0

Sure!

@Route(layout = MainLayoutView.class)
@PageTitle("Serial Master List")
@PermitAll
public class SearchByDateView extends VerticalLayout {

    private final DataService service;
    private DTO dto;

    private SerialTimeService serialTimeService;

    Grid<SerialNumbers> grid = new Grid<>(SerialNumbers.class);
    Grid<DTO> dtoGrid = new Grid<>(DTO.class);

    DatePicker start = new DatePicker("Start");
    DatePicker end = new DatePicker("End");
    TextField search = new TextField("Search");

    public SearchByDateView(DataService service) {
        this.service = service;
        DataController dataController = new DataController(service);
        dataController.init();
        setSizeFull();

        Button filter = new Button("Filter", event -> filterByDate());
        Button reset = new Button("Reset", event -> reset());
        search.addValueChangeListener(event -> textSearch());

        /*
         * Checking to see if the start date or end date has a value change,
         * and if it does, disable the search text field. This will prevent searching while a date range is selected.
         * Need to implement searching against the selected date range at some point.
         */
        start.addValueChangeListener(event -> {
            LocalDate startDate = event.getValue();
            LocalDate endDate = event.getValue();
            if(startDate != null && endDate !=null) {
                search.setEnabled(false);
            } else {
                search.setEnabled(true);
            }
        });

        end.addValueChangeListener(event -> {
            LocalDate startDate = event.getValue();
            LocalDate endDate = event.getValue();
            if(startDate != null && endDate !=null) {
                search.setEnabled(false);
            } else {
                search.setEnabled(true);
            }
        });

        reset.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
        HorizontalLayout filterBar = new HorizontalLayout(start, end, search, reset, filter);
        filterBar.setAlignItems(Alignment.END);
        add(filterBar);

        VerticalLayout layout = new VerticalLayout(createGrid());
        layout.setSizeFull();
        layout.setPadding(false);
        layout.setSpacing(false);

        add(createGrid());

        filter.addClickShortcut(Key.ENTER);
        filterByDate();
    }```
 private Component createGrid() {
        dtoGrid = new Grid<>(DTO.class, false);
        SerialRoot serialRoot = service.getSerial();
        List<SerialNumbers> serialNumbers = serialRoot.getSerial_numbers();
        List<DTO> dtoList = new ArrayList<>();
        for(SerialNumbers serialNumber : serialNumbers) {
            int skip = 0;
            while(true) {
                OrderRoot orderRoot = service.getOrder(serialNumber.getOrder_number(), skip);
                if(orderRoot == null) {
                    break;
                } else if(orderRoot.getOrder_number() == serialNumber.getOrder_number()) {
                    dto.setName(orderRoot.getStash().getShipping_information().getName());
                    dtoList.add(new DTO(serialRoot, orderRoot));
                    skip +=1;
                }
            }
        }

        dtoGrid.setItems(dtoList);
        return dtoGrid;
    }

    private void filterByDate() {

        // implement search functionality that will search against already filtered serials by date

        if(start.getValue() != null && end.getValue() != null) {
           grid.setItems(service.listEntries(start.getValue(), end.getValue()));
            grid.scrollToStart();
        } else if(search.getValue() != null) {
            textSearch();
        }
    }

    private void textSearch() {
        search.setPlaceholder("Search");
        search.addValueChangeListener(event -> {
            String searchTerm = event.getValue();
            List<SerialNumbers> filteredItems = service.getSerial().getSerial_numbers().stream()
                    .filter((serialNumbers -> serialNumbers.getSerial_number().contains(searchTerm))).collect(Collectors.toList());
            grid.setItems(filteredItems);
        });
    }

    //reset the current search fields
    private void reset() {
        grid.setItems(service.getSerial().getSerial_numbers());
        start.setValue(null);
        end.setValue(null);
        search.setValue("");
    }
}```

The cause seems to be in the Data Service class

Can you share also that class?

@Service
public class DataService {

    private final WebClient webClient;

    private LocalDateTime todaysDate = java.time.LocalDateTime.now();

    private String token = "";
    private String auth = "Authorization";
    private String skuT = "SKULabsToken ";
    //"/order/get_scanned_serial_numbers?start=2022-11-09T00:00:00Z&end=2022-11-10T00:00:00Z"

    private LocalDate start;
    private LocalDate end;

    public DataService(WebClient.Builder builder) {
        webClient = builder.baseUrl("https://api.skulabs.com/").build();
    }

    public SerialRoot getSerial() {
        return webClient
                .get()
                .uri("/order/get_scanned_serial_numbers?start=2021-11-29T00:00:00Z&end=%s".formatted(todaysDate))
                .header(auth, skuT + token)
                .retrieve()
                .bodyToMono(SerialRoot.class)
                .block();
    }

    public OrderRoot getOrder(String orderNumber, int skip) {
        return webClient
                .get()
                .uri(uriBuilder -> uriBuilder
                        .path("/order/get")
                        .queryParam("selector", "{\"order_number\":\"" + orderNumber + "\"}")
                        .queryParam("limit", 1)
                        .queryParam("skip", skip)
                        .build())
                .header(auth, skuT + token)
                .retrieve()
                .bodyToMono(OrderRoot.class)
                .block();
    }

    public List<SerialNumbers> listEntries(LocalDate start, LocalDate end) {

        LocalDate currentDate = LocalDate.now();
        Notification datePickerErrorNotification = new Notification();

        //checking if start and end is null, if it is then return pagination list
        if(start == null && end  == null) {
            SerialRoot serialRoot = getSerial();
            return serialRoot.getSerial_numbers();

            //checking if the start date is after the current date plus one day, if it is throw error message.
        } else {
            if(start.isAfter(currentDate.plusDays(1))) {
                dateErrorMsg();
            } else {
                SerialRoot serialRoot = getSerial();
                List<SerialNumbers> serialNumbers = serialRoot.getSerial_numbers().stream()
                        .filter(s -> (s.getTime() != null &&
                                (s.getTime().isEqual(start) || s.getTime().isAfter(start)) &&
                                (s.getTime().isEqual(end) || s.getTime().isBefore(end))))
                        .collect(Collectors.toList());
                return serialNumbers;
            }
        }
        return null;
    }

    public void dateErrorMsg() {
        Notification notification = new Notification();
        notification.addThemeVariants(NotificationVariant.LUMO_ERROR);
        notification.setPosition(Notification.Position.TOP_CENTER);

        notification.setDuration(10000);

        Div text = new Div(new Text("ERROR: Start date cannot be more than one day in the future"));

        HorizontalLayout layout = new HorizontalLayout(text);
        layout.setAlignItems(FlexComponent.Alignment.CENTER);

        notification.add(layout);
        notification.open();
    }
}```

listEntries is not currently used. for future search functions

So, it seems like that spring is considering {order_number} as a variable

I suppose you should somehow escape it

Try to URL encode the json contents

Take a look here

So that prints the uri as https://api.skulabs.com/order/get?selector=%257B%2522order_number%2522%253A%2522E1163%2522%257D&limit=1&skip=0' now

Where it should look like:
https://api.skulabs.com/order/get?selector={"order_number":"E000"}