0

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");
}

}

Jinx3y
  • 53
  • 2
  • 8
  • Did you try the Gluon samples? There is a sample using [RestClient](https://github.com/gluonhq/gluon-samples/blob/master/gluon-connect-rest-provider/src/main/java/com/gluonhq/samples/connect/rest/RestObjectView.java#L84) that uses a json converter. Then you can check if [getInputStream()](https://github.com/gluonhq/gluon-samples/blob/master/gluon-connect-rest-provider/src/main/java/com/gluonhq/samples/connect/rest/SingleItemInputConverter.java#L48) works or not. – José Pereda Jan 15 '22 at 18:39
  • I have tried the samples (gluon-connect-rest-provider)...they work fine for GETting info from StackOverflow, but do nothing to explain a POST call with attached message body - or did I miss something? I have tried to "rewire" the sample using what I need, but have so far had no success – Jinx3y Jan 17 '22 at 09:54
  • Have you tried setting the [`contentType`](https://docs.gluonhq.com/connect/javadoc/2.0.1/com.gluonhq.connect/com/gluonhq/connect/provider/RestClient.html#contentType(java.lang.String)) for the rest client (not only via `header`)? – José Pereda Jan 17 '22 at 10:08
  • I have tried both setting it via .contentType("") and via.header("","") (as well as omitting it altogether). – Jinx3y Jan 17 '22 at 10:14
  • Hmm, if you use POST, you should be _writing_ the object, not _reading_ it, shouldn't you? Let me add a simple POST sample – José Pereda Jan 17 '22 at 10:44

1 Answers1

1

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}
José Pereda
  • 44,311
  • 7
  • 104
  • 132
  • Thanks for that. I also discovered that the old properties file I was using had single-quotes around the values (causing a "no protocol" error). Fixed that, I am at least getting an object back via the GET call (though I need a converter because it is comprised of multiple objects). The POST call, I have to work on...I am sending an ElasticSearch request - no idea how that could be made into an entity/class...I am still trying to learn how to do all of this. I had thought the Gluon libraries might help to simplify the process. (see example in initial post). – Jinx3y Jan 17 '22 at 13:32