Searching by product name and receiving list of stores that sell matching items (Java)

In this use case, the user types the name of a product on a search bar and expects a list of stores that sell the items matching his search. This sort of result appears in the case of retail aggregator BPPs that have onboarded multiple sellers on their platform

Search

Code snippets

Client calls the BAP server to trigger search:

    @PostMapping("/local_retail/search_by_product_name")
    public ResponseEntity searchByProductName(
            @RequestHeader HttpHeaders headers,
            @RequestBody ClientSearchRequest request) {
        var response = bapApplicationService.generateSearchRequest(request, headers);
        return ResponseEntity.ok(response);
    }

BAP server generates the protocol request body

/*
Example Request JSON:
{
    "context": {
        "domain": "local_retail",
        "country": "IND",
        "city": "std:080",
        "action": "search",
        "core_version": "0.9.2",
        "bap_id": "https://mock_bap.com/",
        "bap_uri": "https://mock_bap.com/beckn/",
        "transaction_id": "1239890342",
        "message_id": "123793824",
        "timestamp": "2021-03-23T10:00:40.065Z"
    },
    "message": {
        "intent" : {
            "item": {
                "descriptor" : {
                    "name" : "Bread and Milk"
                }
            },
            "fulfillment": {
                "end": {
                    "location": {
                        "gps": "12.4535445,77.9283792"
                    }
                }
            }
        }
    }
}
*/

    public Response generateSearchRequest(ClientSearchRequest request, HttpHeaders headers) {

        var messageId = UUID.randomUUID().toString();

        // Check and create transaction id if not passed
        var txnId = StringUtils.hasText(request.getTransactionId())
                ? request.getTransactionId()
                : UUID.randomUUID().toString();

        // Construct the request
        var context = Context.builder().domain(request.getDomain())
                .action(Context.ActionEnum.search)
                .messageId(messageId)
                .transactionId(txnId)
                .transactionId(UUID.randomUUID().toString())
                .timestamp(new Date().toString())
                .build();
        var intentBuilder = Intent.builder();

        // Construct item object for the request from product's name
        intentBuilder = intentBuilder.item(ClientAssembler.of(request.getItem()));

        var searchRequest = SearchRequest.builder()
                .context(context)
                .message(SearchMessage.builder()
                        .intent(intentBuilder.build()).build()).build();

        return invokeSearch(searchRequest, headers);
    }
}

BAP server calls protocol search to the network

    public Response invokeSearch(SearchRequest request, HttpHeaders headers) {

        // Call to look up function which returns the the public key and BPP/BG Endpoint to be called
        var url = lookUp(headers);

        //Call BPP Search api
        var searchResponse = apiClient.post(url[0] + Context.ActionEnum.search,
                constructRequestHeaders(),
                request,
                Response.class);
        if (responseEntity.getBody() == null || responseEntity.getBody().getError() != null ||
                "NACK".equals(responseEntity.getBody().getMessage().getAck().getStatus())) {
            // Return custom error to the client
            return null;
        }
        return searchResponse.getBody();
    }

BAP receives protocol on_search

/*
Example Response JSON:
{
    "context": {
        "domain": "local_retail",
        "country": "IND",
        "city": "std:080",
        "action": "on_search",
        "core_version": "0.9.2",
        "bap_id": "https://mock_bap.com/",
        "bap_uri": "https://mock_bap.com/beckn/",
        "transaction_id": "1239890342",
        "message_id": "123793824",
        "timestamp": "2021-06-23T09:53:38.872Z"
    },
    "message": {
        "catalog": {
            "bpp/descriptor": {
                "name": "Shop EZ"
            },
            "bpp/providers": [
                {
                    "id": "./retail.kirana/ind.blr/[email protected]",
                    "descriptor": {
                        "name": "Pooja Stores"
                    },
                    "locations": [
                        {
                            "id": "./retail.kirana/ind.blr/[email protected]_location",
                            "gps": "12.9349377,77.6055586"
                        }
                    ]
                },
                {
                    "id": "./retail.kirana/ind.blr/[email protected]",
                    "descriptor": {
                        "name": "Nilgiris"
                    },
                    "locations": [
                        {
                            "id": "./retail.kirana/ind.blr/[email protected]_location",
                            "gps": "12.9349406,77.6208795"
                        }
                    ]
                },
                {
                    "id": "./retail.kirana/ind.blr/[email protected]pp.shopez.com",
                    "descriptor": {
                        "name": "Food Mall"
                    },
                    "locations": [
                        {
                            "id": "./retail.kirana/ind.blr/[email protected]_location"
                        }
                    ]
                }
            ]
        }
    }
}
*/
    @PostMapping("/bap/on_search")
    public ResponseEntity onSearch(
            @RequestHeader HttpHeaders headers,
            @RequestBody OnSearchRequest request) {
        var response = bapCallbackApplicationService.onSearch(request, headers);
        return ResponseEntity.ok(response);
    }

    public Response onSearch(OnSearchRequest request, HttpHeaders headers) {
        // Validate the headers received
        var isHeadersValid = validateHeaders(headers);
        // Construct and return error if the received headers are invalid
        if (!isHeadersValid) return null;

        // Store the data received based on message id for the client to poll
        saveToDB(request);

        return Response.of("ACK", null);
    }

Client polls BAP to get the on_search results

    // Endpoint for the client to poll the search data based on the message id
    @GetMapping("/local_retail/on_search")
    public ResponseEntity searchByMessageId(
            @PathVariable(ClientRoutes.PARAM_MESSAGE_ID) String messageId,
            @RequestHeader HttpHeaders headers) {
        var data = bapApplicationService.get(messageId);
        return ResponseEntity.ok(data);
    }