javaelasticsearchjavafxgluonfx

Query with ElasticSearch body using GluonhqConnect.Provider.RestClient


I am trying to use the com.gluonhq.connect.provider.RestClient to query an API that is Elastic Search enabled.

Maven:
    <dependency>
        <groupId>com.gluonhq</groupId>
        <artifactId>connect</artifactId>
        <version>2.0.1</version>
    </dependency>

I have attempted to make a request using the following:

private GluonObservableObject<Item> getData() {
    RestClient myApiClient = RestClient.create()
            .method("POST")
            .host(applicationProperties.getAPIURL())
            .path("search")
            .header("accept", "application/json")
            .header("Content-type", "application/json")
            .queryParam("private_key", applicationProperties.getAPIKEY())
            .dataString(ITEMREQUEST);

    return DataProvider.retrieveObject(
            myApiClient.createObjectDataReader(Item.class));
}

...where the dataString is a json-formatted String representing the body of the request (I can assume the request is correctly formatted because I tested it in Postman and the request returned the expected data). If it helps, here is the elasticsearch request body:

 {
  "indexes": "idx1, idx2",
  "columns": "col1,col2,col3,col4,col5",
  "body": {
    "query": {
      "bool": {
        "must": [
          {
            "wildcard": {
              "NameCombined_en": "*"
            }
          }
        ],
        "filter": [
          {
            "range": {
              "ID": {
                "gte": "20000"
              }
            }
          }
        ]
      }
    },
    "from": 0,
    "size": 100
  }
}

The problem I have is that the (gluon) RestClient and, by extension, the DataProvider.retrieveObject method return exactly...nothing.

I'm pretty sure I'm doing something wrong and I'm fairly certain it is the .dataString() method (which requires an "entity"), but I have not found an alternative to use as a way of passing the body into the request.

The reasoning behind using the com.gluonhq.connect library is to avoid having to also create my own Observable lists (by hand) - the library automatically spits one out, providing the data is suitably formatted...and present. At least, that's my understanding of it.

Can someone point me in the right direction? I have found no indication or explanation of how to do POST requests with this library.

UPDATE 20220117

Main.java

public class Main extends Application {
ApplicationProperties applicationProperties = new ApplicationProperties();
private static final String RESTLIST_VIEW = HOME_VIEW;
private static final String RESTOBJECT_VIEW = "RestObjectView";


private final AppManager appManager = AppManager.initialize(this::postInit);

@Override
public void init() {
    appManager.addViewFactory(RESTOBJECT_VIEW, () -> new RestObjectView(applicationProperties.APIURL, applicationProperties.APIKEY));

    updateDrawer();
}

@Override
public void start(Stage stage) {
    appManager.start(stage);
}

private void postInit(Scene scene) {
    Swatch.BLUE.assignTo(scene);

    ((Stage) scene.getWindow()).getIcons().add(new Image(Objects.requireNonNull(Main.class.getResourceAsStream("/icon.png"))));
}

private void updateDrawer() {
    NavigationDrawer navigationDrawer = appManager.getDrawer();
    NavigationDrawer.Header header = new NavigationDrawer.Header("Gluon Mobile", "Gluon Connect Rest Provider Sample",
            new Avatar(21, new Image(getClass().getResourceAsStream("/icon.png"))));
    navigationDrawer.setHeader(header);
    NavigationDrawer.Item listItem = new NavigationDrawer.Item("List Viewer", MaterialDesignIcon.VIEW_LIST.graphic());
    NavigationDrawer.Item objectItem = new NavigationDrawer.Item("Object Viewer", MaterialDesignIcon.INSERT_DRIVE_FILE.graphic());
    navigationDrawer.getItems().addAll(listItem, objectItem);
    navigationDrawer.selectedItemProperty().addListener((obs, oldItem, newItem) -> {
        if (newItem.equals(listItem)) {
            appManager.switchView(RESTLIST_VIEW);
        } else if (newItem.equals(objectItem)) {
            appManager.switchView(RESTOBJECT_VIEW);
        }
    });
}

public static void main(String[] args) {
    System.setProperty("javafx.platform", "Desktop");

    launch(args);
}

RestObjectView.java

public class RestObjectView extends View {

public RestObjectView(String apiurl, String apikey) {

    Label lbItemId = new Label();
    Label lbName = new Label();
    Label lbDescription = new Label();
    Label lbLvlItem = new Label();
    Label lbLvlEquip = new Label();

    GridPane gridPane = new GridPane();
    gridPane.setVgap(5.0);
    gridPane.setHgap(5.0);
    gridPane.setPadding(new Insets(5.0));
    gridPane.addRow(0, new Label("Item ID:"), lbItemId);
    gridPane.addRow(1, new Label("Name:"), lbName);
    gridPane.addRow(2, new Label("Description:"), lbDescription);
    gridPane.addRow(3, new Label("Item Level:"), lbLvlItem);
    gridPane.addRow(4, new Label("Equip Level:"), lbLvlEquip);
    gridPane.getColumnConstraints().add(new ColumnConstraints(75));

    lbItemId.setWrapText(true);
    lbName.setWrapText(true);
    lbDescription.setWrapText(true);
    lbLvlItem.setWrapText(false);
    lbLvlEquip.setWrapText(false);

    setCenter(gridPane);

    // create a RestClient to the specific URL
    RestClient restClient = RestClient.create()
            .method("POST")
            .host(apiurl)
            .path("Item")
            .queryParam("private_key", apikey);

    // create a custom Converter that is able to parse the response into a single object
    InputStreamInputConverter<Item> converter = new SingleItemInputConverter<>(Item.class);

    // retrieve an object from the DataProvider
    GluonObservableObject<Item> item = DataProvider.retrieveObject(restClient.createObjectDataReader(converter));

    // when the object is initialized, bind its properties to the JavaFX UI controls
    item.initializedProperty().addListener((obs, oldValue, newValue) -> {
        if (newValue) {
            lbItemId.textProperty().bind(item.get().itemIdProperty().asString());
            lbName.textProperty().bind(item.get().nameProperty());
            lbDescription.textProperty().bind(item.get().descriptionProperty());
            lbLvlItem.textProperty().bind(item.get().levelItemProperty().asString());
            lbLvlEquip.textProperty().bind(item.get().levelEquipProperty().asString());
        }
    });
}

@Override
protected void updateAppBar(AppBar appBar) {
    appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> getAppManager().getDrawer().open()));
    appBar.setTitleText("Rest Object Viewer");
}

}


Solution

  • This is a quick demo of RestClient with GET and POST:

    ToDoItem class:

    public class ToDoItem {
        private int userId;
        private int id;
        private String title;
        private boolean completed;
    
        public int getUserId() {
            return userId;
        }
    
        public void setUserId(int userId) {
            this.userId = userId;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getTitle() {
            return title;
        }
    
        public void setTitle(String title) {
            this.title = title;
        }
    
        public boolean isCompleted() {
            return completed;
        }
    
        public void setCompleted(boolean completed) {
            this.completed = completed;
        }
    
        @Override
        public String toString() {
            return "ToDoItem{" +
                    "userId=" + userId +
                    ", id=" + id +
                    ", title='" + title + '\'' +
                    ", completed=" + completed +
                    '}';
        }
    }
    

    GET test:

    public void getTest() {
    
            RestClient restClient = RestClient.create()
                    .method("GET")
                    .host("http://jsonplaceholder.typicode.com")
                    .path("/todos/1");
    
            GluonObservableObject<ToDoItem> singleToDoItem = 
                DataProvider.retrieveObject(
                    restClient.createObjectDataReader(ToDoItem.class));
    
            singleToDoItem.setOnSucceeded(e -> 
                System.out.println("ToDoItem successfully retrieved: " + singleToDoItem.get()));
            singleToDoItem.setOnFailed(e -> {
                System.out.println("ToDoItem GET error");
                if (singleToDoItem.getException() != null) {
                    singleToDoItem.getException().printStackTrace();
                }
            });
        }
    

    POST test:

    public void postTest() {
            ToDoItem toDoItem = new ToDoItem();
            toDoItem.setCompleted(Math.random() > 0.5);
            toDoItem.setTitle(UUID.randomUUID().toString());
            toDoItem.setUserId(1);
    
            // write a new todo item to a rest source
            RestClient restClient = RestClient.create()
                    .method("POST")
                    .contentType("application/json")
                    .host("http://jsonplaceholder.typicode.com")
                    .path("/todos");
    
            GluonObservableObject<ToDoItem> obsToDoItem = 
                    DataProvider.storeObject(toDoItem,
                        restClient.createObjectDataWriter(ToDoItem.class));
    
            obsToDoItem.setOnSucceeded(e -> 
                System.out.println("ToDoItem successfully written: " + obsToDoItem.get()));
            obsToDoItem.setOnFailed(e -> {
                    System.out.println("ToDoItem POST error");
                    if (obsToDoItem.getException() != null) {
                        obsToDoItem.getException().printStackTrace();
                    }
                });
        }
    

    Running both should give you something like this:

    ToDoItem successfully retrieved: ToDoItem{userId=1, id=1, title='delectus aut autem', completed=false}
    
    ToDoItem successfully written: ToDoItem{userId=1, id=201, title='6977035b-7a0c-4e6a-82e5-141b414db92a', completed=false}