REST Client: fetching data and populating a table [message #1839291] |
Thu, 18 March 2021 20:14 |
J D Messages: 110 Registered: February 2021 |
Senior Member |
|
|
Hello everyone,
I want to fetch data from a public API and use it to first populate a table and then fill a form when a row in the table is clicked. My problem is I cannot put all the pieces together in order.
The full path to the public data source on the Open Data Paris website is
https://opendata.paris.fr/api/records/1.0/search/?dataset=liste_des_prenoms
and it fetches 10 records (in JSON format) with the following fields: nombre, annee, nombre_total_cumule, sexe, prenoms
Here is what I've done so far:
a) My EntityDo
public class FirstNameEntityDo extends DoEntity {
@AttributeName("nombre")
public DoValue<String> number() {
return doValue("nombre");
}
@AttributeName("annee")
public DoValue<String> year() {
return doValue("annee");
}
@AttributeName("nombre_total_cumule")
public DoValue<String> cumulativeTotal() {
return doValue("nombre_total_cumule");
}
@AttributeName("sexe")
public DoValue<String> sex() {
return doValue("sexe");
}
@AttributeName("prenoms")
public DoValue<String> firstName() {
return doValue("prenoms");
}
public FirstNameEntityDo withName(String string) {
// TODO Auto-generated method stub
return null;
}
}
b) The RestClientHelper
public class FirstNameRestClientHelper extends AbstractRestClientHelper {
@Override
protected String getBaseUri() {
return "https://opendata.paris.fr/api/";
}
@Override
protected RuntimeException transformException(RuntimeException e, Response response) {
if (response != null && response.hasEntity()) {
ErrorDo error = response.readEntity(ErrorResponse.class).getError();
throw new VetoException(error.getMessage()).withTitle(error.getTitle());
}
return e;
}
}
c) The ResourceClient
public class FirstNameResourceClient implements IRestResourceClient {
// example full path
// "https://opendata.paris.fr/api/records/1.0/search/?dataset=liste_des_prenoms&refine.prenoms=Anne-Sophie"
protected static final String RESOURCE_PATH = "/records/1.0/search/";
protected FirstNameRestClientHelper helper() {
return BEANS.get(FirstNameRestClientHelper.class);
}
// Get a list of firstnames
public FirstNameEntityDo getFirstNameEntity() {
WebTarget target = helper().target(RESOURCE_PATH).queryParam("dataset", "liste_des_prenoms");
return target.request().accept(MediaType.APPLICATION_JSON).get(FirstNameEntityDo.class);
}
// Get a particular firstname e.g "Anne-Sophie" from a list of firstnames
public FirstNameEntityDo getFirstNameEntity(String firstname) {
WebTarget target = helper().target(RESOURCE_PATH).queryParam("dataset", "liste_des_prenoms")
.queryParam("refine.prenoms", firstname);
return target.request().accept(MediaType.APPLICATION_JSON).get(FirstNameEntityDo.class);
}
public FirstNameEntityDo getFirstNameEntityCustomExceptionHandling() {
WebTarget target = helper().target(RESOURCE_PATH, this::transformCustomException)
.queryParam("dataset", "liste_des_prenoms");
return target.request().accept(MediaType.APPLICATION_JSON).get(FirstNameEntityDo.class);
}
protected RuntimeException transformCustomException(RuntimeException e, Response r) {
if (r != null && r.hasEntity() && MediaType.TEXT_PLAIN_TYPE.equals(r.getMediaType())) {
String message = r.readEntity(String.class);
throw new VetoException(message);
}
return e;
}
}
d) Finally in the FirstNamePage.java class, I have the following
@Override
protected void execLoadData(SearchFilter filter) {
importPageData(BEANS.get(IFirstNameService.class).getFirstNameTableData(filter)); <--- THIS IS NOT RELEVANT FOR REST CALLS
}
I know that the execLoadData is incorrect but I do not know how to correct it.
I want to fill the FirstNameTable immediately after I click on the tile. However I need some help with tying all the loose ends I posted above together.
Thanks a lot for your kind assistance.
Cheers,
JD
[Updated on: Fri, 19 March 2021 09:39] Report message to a moderator
|
|
|
Re: REST Client: fetching data and populating a table [message #1839307 is a reply to message #1839291] |
Fri, 19 March 2021 07:58 |
|
Hi JD
You say you want to click on a tile, so I assume you have a form which has a tile-grid with tiles. Is that correct?
The AbstractTileGrid has a method #execTileClick(T tile, MouseButton mouseButton) which handles a click on a tile. This is where you would call your REST service.
When you work with REST services you don't necessarily need to work with the generated FormDatas or TableDatas. These classes are intended for the classic Scout approach, where the Scout UI sends form-data to the Scout backend. In cases where your UI directly makes a REST call you don't need them.
Btw: I don't know the REST service you call, but to me it looks like the call should return a _list_ of first names, and not only a single first-name entity.
@Override
protected void execTileClick(JdTile tile, MouseButton mouseButton) {
// Optional: maybe you need some data from the clicked tile to make a specific REST call?
// Object lookupId = tile.getLookupId();
List<FirstNamesEntityDo> firstNames = BEANS.get(IFirstNameService.class).getFirstNames(lookupId);
FirstNameTable firstNameTable = getFirstNameTable();
firstNameTable.deleteAllRows();
firstNames.stream().forEach(
firstName -> addFirstNameTableRow(firstNameTable, firstName));
}
protected List<ITableRow> addFirstNameTableRow(FirstNameTable firstNameTable, FirstNameEntityDo firstName) {
ITableRow row = firstNameTable.addRow();
firstNameTable.getNombreColumn().setValue(row, firstName.getNombre());
firstNameTable.getSexeColumn().setValue(row, firstName.getSexe());
//...
}
Most likely this will not compile, as I hacked everything into the forum editor here, but it should give you an idea of how to proceed.
Cheers,
André
Eclipse Scout Homepage | Documentation | GitHub
|
|
|
Re: REST Client: fetching data and populating a table [message #1839698 is a reply to message #1839307] |
Thu, 25 March 2021 23:28 |
J D Messages: 110 Registered: February 2021 |
Senior Member |
|
|
Hello there Andre,
Thanks a lot for your suggestions. Unfortunately, I'm still having problems with retrieving the data. This is what my code looks like now:
a) FirstNameResourceClient for fetching the data
public class FirstNameResourceClient implements IRestResourceClient {
// example full path
// "https://opendata.paris.fr/api/records/1.0/search/?dataset=liste_des_prenoms&refine.prenoms=Anne-Sophie"
protected static final String RESOURCE_PATH = "/records/1.0/search/";
protected FirstNameRestClientHelper helper() {
return BEANS.get(FirstNameRestClientHelper.class);
}
// Get a list of firstnames
public List<FirstNameEntityDo> getFirstNameEntity() {
WebTarget target = helper().target(RESOURCE_PATH).queryParam("dataset", "liste_des_prenoms");
return (List<FirstNameEntityDo>) target.request().accept(MediaType.APPLICATION_JSON)
.get(FirstNameEntityDo.class);
}
// Get a particular firstname from a list of firstnames
public List<FirstNameEntityDo> getFirstNameEntity(String firstname) {
WebTarget target = helper().target(RESOURCE_PATH).queryParam("dataset", "liste_des_prenoms")
.queryParam("refine.prenoms", firstname);
return (List<FirstNameEntityDo>) target.request().accept(MediaType.APPLICATION_JSON)
.get(FirstNameEntityDo.class);
}
public FirstNameEntityDo getFirstNameEntityCustomExceptionHandling() {
WebTarget target = helper().target(RESOURCE_PATH, this::transformCustomException)
.queryParam("dataset", "liste_des_prenoms");
return target.request().accept(MediaType.APPLICATION_JSON).get(FirstNameEntityDo.class);
}
protected RuntimeException transformCustomException(RuntimeException e, Response r) {
if (r != null && r.hasEntity() && MediaType.TEXT_PLAIN_TYPE.equals(r.getMediaType())) {
String message = r.readEntity(String.class);
throw new VetoException(message);
}
return e;
}
}
b) FirstNamePage - the execLoadData() method fetches the data from the server
@Data(FirstNameTablePageData.class)
public class FirstNamePage extends AbstractPageWithTable<Table> {
// Fetch the list of names from the REST server
@Override
protected void execLoadData(SearchFilter filter) {
List<FirstNameEntityDo> firstNames =
BEANS.get(FirstNameResourceClient.class).getFirstNameEntity();
// FirstNameTable firstNameTable = FirstNameTable.getFirstNameTable();
// firstNameTable.deleteAllRows();
// firstNames.stream().forEach(firstName -> addFirstNameTableRow(firstNameTable, firstName));
}
@Override
protected String getConfiguredTitle() {
// TODO [jide] verify translation
return TEXTS.get("FirstName");
}
@Override
protected boolean getConfiguredLeaf() {
return true;
}
public class FirstNameTable extends AbstractTable {
// container class to hold columns and other elements for this table page
public FirstNameTable getFirstNameTable() {
return new FirstNameTable();
}
// // This action gets executed when the user presses Enter on a table row or double clicks on a
// // table row
@Override
protected Class<? extends IMenu> getConfiguredDefaultMenu() {
return EditMenu.class;
}
public SexColumn getSexColumn() {
return getColumnSet().getColumnByClass(SexColumn.class);
}
public YearColumn getYearColumn() {
return getColumnSet().getColumnByClass(YearColumn.class);
}
public FirstNameColumn getFirstNameColumn() {
return getColumnSet().getColumnByClass(FirstNameColumn.class);
}
public CumulativeTotalPerYearColumn getCumulativeTotalPerYearColumn() {
return getColumnSet().getColumnByClass(CumulativeTotalPerYearColumn.class);
}
public NumberOfDeclaredFirstNamesColumn getNumberOfDeclaredFirstNamesColumn() {
return getColumnSet().getColumnByClass(NumberOfDeclaredFirstNamesColumn.class);
}
// Test fetching JSON data
@Order(500)
public class SearchMenu extends AbstractSimpleSearchMenu {
@Override
protected void execAction() {
List<FirstNameEntityDo> firstNames =
BEANS.get(FirstNameResourceClient.class).getFirstNameEntity();
System.out.println(firstNames); // <---- CRUDE DEBUGGING
}
}
// Three menu items: New, Edit & Delete will be displayed for rows in the table
@Order(1000)
public class NewMenu extends AbstractNewMenu {
@Override
protected String getConfiguredText() {
return TEXTS.get("New");
}
// Including TableMenuType.EmptySpace in the return value activates the "New" menu even when
// no row is selected.
@Override
protected Set<? extends IMenuType> getConfiguredMenuTypes() {
return CollectionUtility.<IMenuType>hashSet(TableMenuType.EmptySpace,
TableMenuType.SingleSelection);
}
@Override
protected void execAction() {
FirstNameForm form = new FirstNameForm();
form.addFormListener(new FirstNameFormListener());
// start the form using its new handler
form.startNew();
}
}
@Order(2000)
public class EditMenu extends AbstractEditMenu {
@Override
protected void execAction() {
FirstNameForm form = new FirstNameForm();
// form.setPartnerContactId(getContactIdColumn().getSelectedValue());
form.addFormListener(new FirstNameFormListener());
// start the form using its modify handler
form.startModify();
}
}
@Order(3000)
public class DeleteMenu extends AbstractDeleteMenu {
}
private class FirstNameFormListener implements FormListener {
@Override
public void formChanged(FormEvent e) {
// reload page to reflect new/changed data after saving any changes
if (FormEvent.TYPE_CLOSED == e.getType() && e.getForm().isFormStored()) {
reloadPage();
}
}
}
@Order(1000)
public class NumberOfDeclaredFirstNamesColumn extends AbstractStringColumn {
@Override
protected String getConfiguredHeaderText() {
return TEXTS.get("NumberOfDeclaredFirstNames");
}
@Override
protected int getConfiguredWidth() {
return 200;
}
}
@Order(2000)
public class YearColumn extends AbstractStringColumn {
@Override
protected String getConfiguredHeaderText() {
return TEXTS.get("Year");
}
@Override
protected int getConfiguredWidth() {
return 100;
}
}
@Order(3000)
public class CumulativeTotalPerYearColumn extends AbstractStringColumn {
@Override
protected String getConfiguredHeaderText() {
return TEXTS.get("CumulativeTotalPerYear");
}
@Override
protected int getConfiguredWidth() {
return 250;
}
}
@Order(4000)
public class SexColumn extends AbstractStringColumn {
@Override
protected String getConfiguredHeaderText() {
return TEXTS.get("Sex");
}
@Override
protected int getConfiguredWidth() {
return 100;
}
}
@Order(5000)
public class FirstNameColumn extends AbstractStringColumn {
@Override
protected String getConfiguredHeaderText() {
return TEXTS.get("FirstName");
}
@Override
protected int getConfiguredWidth() {
return 100;
}
}
}
}
The problem is whenever I open the FirstNamePage, I get an error message shown below:
2021-03-25 23:42:57,773 ERROR [scout-model-thread-25 Processing JSON request] org.eclipse.scout.rt.platform.exception.ExceptionHandler.handlePlatformException(ExceptionHandler.java:124) - - MDC[principal=jd, uiSession=1:1t1heup7o5n28vf4cprlg866chbusorsn438ucnch3effuf2ub0h, scoutSession=1b4g3igm04no1cogohs62v02isa3kkme1h7ou3b8clbk0j1ff7i7, jobName=Processing JSON request, cid=ZW2vM5rE6QJ/4]
org.eclipse.scout.rt.platform.util.Assertions$AssertionException: [b]Assertion error: no instance found for query: class [/b]org.eclipse.scout.apps.yougenie.client.rest.mocksamples.opendataparis.firstname.FirstNameRestClientHelper [user=jd, ui.event=action, ui.adapter=Menu[id=28, modelClass=org.eclipse.scout.apps.yougenie.client.mocksamples.opendataparis.firstname.FirstNamePage$Table$SearchMenu, parentId=19]]
at org.eclipse.scout.rt.platform.util.Assertions.fail(Assertions.java:621)
at org.eclipse.scout.rt.platform.util.Assertions.assertNotNull(Assertions.java:87)
at org.eclipse.scout.rt.platform.BEANS.get(BEANS.java:42)
at org.eclipse.scout.apps.yougenie.client.rest.mocksamples.opendataparis.firstname.FirstNameResourceClient.helper(FirstNameResourceClient.java:18)
at org.eclipse.scout.apps.yougenie.client.rest.mocksamples.opendataparis.firstname.FirstNameResourceClient.getFirstNameEntity(FirstNameResourceClient.java:37)
at org.eclipse.scout.apps.yougenie.client.mocksamples.opendataparis.firstname.FirstNamePage$Table$SearchMenu.execAction(FirstNamePage.java:97)
at org.eclipse.scout.rt.client.ui.action.AbstractAction$LocalActionExtension.execAction(AbstractAction.java:656)
at org.eclipse.scout.rt.client.extension.ui.action.ActionChains$ActionActionChain$1.callMethod(ActionChains.java:57)
at org.eclipse.scout.rt.client.extension.ui.action.ActionChains$ActionActionChain$1.callMethod(ActionChains.java:1)
at org.eclipse.scout.rt.shared.extension.AbstractExtensionChain.callChain(AbstractExtensionChain.java:113)
at org.eclipse.scout.rt.client.extension.ui.action.ActionChains$ActionActionChain.execAction(ActionChains.java:60)
at org.eclipse.scout.rt.client.ui.action.AbstractAction.interceptAction(AbstractAction.java:680)
at org.eclipse.scout.rt.client.ui.action.AbstractAction.doActionInternal(AbstractAction.java:352)
at org.eclipse.scout.rt.client.ui.action.AbstractAction.doAction(AbstractAction.java:343)
at org.eclipse.scout.rt.client.ui.action.AbstractAction$P_UIFacade.fireActionFromUI(AbstractAction.java:629)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.eclipse.scout.rt.client.ModelContextProxy.lambda$1(ModelContextProxy.java:49)
at org.eclipse.scout.rt.platform.chain.callable.CallableChain$Chain.continueChain(CallableChain.java:227)
at org.eclipse.scout.rt.platform.transaction.TransactionProcessor.runTxMandatory(TransactionProcessor.java:156)
at org.eclipse.scout.rt.platform.transaction.TransactionProcessor.runTxRequired(TransactionProcessor.java:139)
at org.eclipse.scout.rt.platform.transaction.TransactionProcessor.intercept(TransactionProcessor.java:78)
at org.eclipse.scout.rt.platform.chain.callable.CallableChain$Chain.continueChain(CallableChain.java:222)
at org.eclipse.scout.rt.platform.chain.callable.CallableChain.call(CallableChain.java:170)
at org.eclipse.scout.rt.platform.context.RunContext.call(RunContext.java:158)
at org.eclipse.scout.rt.client.ModelContextProxy.lambda$0(ModelContextProxy.java:49)
at com.sun.proxy.$Proxy31.fireActionFromUI(Unknown Source)
at org.eclipse.scout.rt.ui.html.json.action.JsonAction.handleUiAction(JsonAction.java:159)
at org.eclipse.scout.rt.ui.html.json.action.JsonAction.handleUiEvent(JsonAction.java:151)
at org.eclipse.scout.rt.ui.html.json.JsonEventProcessor.processEvent(JsonEventProcessor.java:52)
at org.eclipse.scout.rt.ui.html.json.JsonEventProcessor.processEvents(JsonEventProcessor.java:37)
at org.eclipse.scout.rt.ui.html.UiSession.processJsonRequestInternal(UiSession.java:817)
at org.eclipse.scout.rt.platform.util.concurrent.Callables.lambda$0(Callables.java:31)
at org.eclipse.scout.rt.platform.chain.callable.CallableChain$Chain.continueChain(CallableChain.java:227)
at org.eclipse.scout.rt.platform.job.internal.ExceptionProcessor.intercept(ExceptionProcessor.java:41)
at org.eclipse.scout.rt.platform.chain.callable.CallableChain$Chain.continueChain(CallableChain.java:222)
at org.eclipse.scout.rt.platform.chain.callable.CallableChain$Chain.continueChain(CallableChain.java:227)
at org.eclipse.scout.rt.platform.transaction.TransactionProcessor.runTxMandatory(TransactionProcessor.java:156)
at org.eclipse.scout.rt.platform.transaction.TransactionProcessor.runTxRequired(TransactionProcessor.java:139)
at org.eclipse.scout.rt.platform.transaction.TransactionProcessor.intercept(TransactionProcessor.java:78)
at org.eclipse.scout.rt.platform.chain.callable.CallableChain$Chain.continueChain(CallableChain.java:222)
at org.eclipse.scout.rt.platform.chain.callable.CallableChain.call(CallableChain.java:170)
at org.eclipse.scout.rt.platform.context.RunContext.call(RunContext.java:158)
at org.eclipse.scout.rt.platform.context.RunContextRunner.intercept(RunContextRunner.java:38)
at org.eclipse.scout.rt.platform.chain.callable.CallableChain$Chain.continueChain(CallableChain.java:222)
at org.eclipse.scout.rt.platform.job.internal.CallableChainExceptionHandler.intercept(CallableChainExceptionHandler.java:33)
at org.eclipse.scout.rt.platform.chain.callable.CallableChain$Chain.continueChain(CallableChain.java:222)
at org.eclipse.scout.rt.platform.chain.callable.CallableChain.call(CallableChain.java:170)
at org.eclipse.scout.rt.platform.job.internal.JobFutureTask.lambda$0(JobFutureTask.java:106)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at org.eclipse.scout.rt.platform.job.internal.JobFutureTask.run(JobFutureTask.java:175)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
at org.eclipse.scout.rt.platform.job.internal.NamedThreadFactory$1.run(NamedThreadFactory.java:63)
It looks like the most important thing to retain is the statement
Assertion error: no instance found for query: class
Can you help me get around this problem?
Thanks a lot,
JD
|
|
|
|
Re: REST Client: fetching data and populating a table [message #1839799 is a reply to message #1839704] |
Mon, 29 March 2021 09:58 |
J D Messages: 110 Registered: February 2021 |
Senior Member |
|
|
Hello there André,
Thanks for your suggestions. I tried it but I was unsuccessful. These are the errors/exceptions I got when I added the annotations @ApplicationScoped and/or @Bean to the class FirstNameRestClientHelper:
a) As is without annotations
Assertion error:
no instance found for query: class org.eclipse.scout.apps.ygclient.rest.mocksamples.opendataparis.firstname.FirstNameRestClientHelper
b) With @ApplicationScoped
BeanCreationException:
Could not create bean [class org.eclipse.scout.apps.ygclient.rest.mocksamples.opendataparis.firstname.FirstNameRestClientHelper]
c) With @ApplicationScoped & @Bean
BeanCreationException:
Could not create bean [class org.eclipse.scout.apps.ygclient.rest.mocksamples.opendataparis.firstname.FirstNameRestClientHelper]
d) With @Bean alone
BeanCreationException:
Could not create bean [class org.eclipse.scout.apps.ygclient.rest.mocksamples.opendataparis.firstname.FirstNameRestClientHelper]
What do I do now?
JD
[Updated on: Mon, 29 March 2021 10:07] Report message to a moderator
|
|
|
|
|
|
|
|
|
|
|
Powered by
FUDForum. Page generated in 0.35715 seconds