From 5558455d91e2e2310ba753b7fafddd3b0caa4db8 Mon Sep 17 00:00:00 2001 From: Stefan Cullmann Date: Fri, 10 Jul 2020 18:03:20 +0200 Subject: [PATCH] Simplifying containment --- ...> Pag-Ext50105.ContactRelationExtension.al | 2 +- Pag-Ext50105.ContectRelationExtension.al | 20 --- app.json | 37 ------ readme.md | 117 +++++------------- 4 files changed, 30 insertions(+), 146 deletions(-) rename Pag-Ext50106.ContactRelationListExtension.al => Pag-Ext50105.ContactRelationExtension.al (77%) delete mode 100644 Pag-Ext50105.ContectRelationExtension.al delete mode 100644 app.json diff --git a/Pag-Ext50106.ContactRelationListExtension.al b/Pag-Ext50105.ContactRelationExtension.al similarity index 77% rename from Pag-Ext50106.ContactRelationListExtension.al rename to Pag-Ext50105.ContactRelationExtension.al index 706b730..0552b5e 100644 --- a/Pag-Ext50106.ContactRelationListExtension.al +++ b/Pag-Ext50105.ContactRelationExtension.al @@ -1,4 +1,4 @@ -pageextension 50106 "ContactRelationListExtension" extends "Contact List" +pageextension 50105 "ContactRelationExtension" extends "Contact List" { layout { diff --git a/Pag-Ext50105.ContectRelationExtension.al b/Pag-Ext50105.ContectRelationExtension.al deleted file mode 100644 index e70096e..0000000 --- a/Pag-Ext50105.ContectRelationExtension.al +++ /dev/null @@ -1,20 +0,0 @@ -pageextension 50105 "ContectRelationExtension" extends "Contact Card" -{ - layout - { - - addafter(General) - { - group("ContactRelGroup") - { - Caption = 'Contact Relations'; - part("Contact Relations"; "Contact Relation Listpart") - { - SubPageLink = "Contact No." = FIELD("No."); - Visible = false; - } - } - - } - } -} \ No newline at end of file diff --git a/app.json b/app.json deleted file mode 100644 index 6f1348e..0000000 --- a/app.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "id": "6d2eaf41-642c-41ff-81ce-c3cc06d9d3c4", - "name": "api navigation", - "publisher": "publisher", - "version": "1.0.0.0", - "brief": "", - "description": "", - "privacyStatement": "", - "EULA": "", - "help": "", - "url": "", - "logo": "", - "dependencies": [ - { - "id": "63ca2fa4-4f03-4f2b-a480-172fef340d3f", - "publisher": "Microsoft", - "name": "System Application", - "version": "16.0.0.0" - }, - { - "id": "437dbf0e-84ff-417a-965d-ed2bb9650972", - "publisher": "Microsoft", - "name": "Base Application", - "version": "16.0.0.0" - } - ], - "screenshots": [], - "platform": "16.0.0.0", - "idRanges": [ - { - "from": 50100, - "to": 50149 - } - ], - "showMyCode": true, - "runtime": "5.0" -} \ No newline at end of file diff --git a/readme.md b/readme.md index e8c68df..4e75209 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,6 @@ # Business Central: Navigation in Webservices and API -This is a spike project to explore both the REST Webservices and the new custom API of Business Central. My interest here is especially in the O-Data navigation features. I would like to query whole entity trees with only a single call using $expand. The [documentation](https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/webservices/use-containments-associations) I have found so far on the subject is extremely scarce and also outdated. +This is a spike project to explore both the REST Webservices and the new custom API of Business Central. My interest here is especially in the OData navigation features. I would like to query whole entity trees with only a single call using $expand. + ## Setup ### A simple relation beween contacts @@ -126,6 +127,10 @@ It also contains now our definition of Contact Relations: The name of the entity type is defined by the name of the service and not by the name of page or table. Fields are renamed to its Pascal case representation. +The [documentation](https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/webservices/use-containments-associations) about associations and containment was not quite clear for me as a beginner in AL. + +That's why I'll try out the concept in the next sections. + ### Associations OData also allows to include related resources within one single request using the query option `$expand`. In case of contact relations, it would be nice to include the contacts for Contact_No and Relation_to_Contact_No. @@ -220,42 +225,47 @@ page 50104 "Contact Relation Listpart" ``` -This list part has now to be added to a page, for example `page5050.Contact Card` via a page extension. +This list part has now to be added to the `page5052.Contact`(List) via a page extension. ```AL -pageextension 50105 "ContectRelationExtension" extends "Contact Card" +pageextension 50105 "ContactRelationExtension" extends "Contact List" { layout { - addafter(General) + addafter(Control1) { group("ContactRelGroup") { - Caption = 'Contact Relations'; - - part("Contact Relations"; "Contact Relation Listpart") + part("Relations"; "Contact Relation Listpart") { SubPageLink = "Contact No." = FIELD("No."); - Visible = false; } } } } } ``` +The group `ContactRelGroup` prevents that the ContactRelation to appear as list within UI as part of tte contact list. -After publishing `page5050.Contact Card` as web service ContactCard, it is possible to include the contactrelation of a contact: +This page is already published for the association, so +this adds immediately the following navigaton property to the contact type: -`...ContactCard('KT200038')?$expand=ContactCardContact_Relations` returns +```xml + +``` +The name of the property combines the name of the service and the name of the part. + +`...Contact('KT200038')?$expand=ContactRelations` returns ```js { - "@odata.context": "http://bc160:7048/BC/ODataV4/$metadata#Company('Cronus%20AG')/ContactCard/$entity", - "@odata.etag": "W/\"JzQ0O1FTc0xuRWpQcUMrU0t0VFJRWFFwZzJiNnBacEU3U3Jvd2dCRGVKam5pLzQ9MTswMDsn\"", + "@odata.context": "http://bc160:7048/BC/ODataV4/$metadata#Company('Cronus%20AG')/Contact/$entity", + "@odata.etag": "W/\"JzQ0O3gwV05ZaVMyVElhTFgzZjVwWVVUdXhNUm92TWt4QWZqR2twQURVYTBIOHc9MTswMDsn\"", "No": "KT200038", "Name": "Karen Berg", - // ...a lot more fields ... - "ContactCardContact_Relations": [ + "Company_Name": "DanMøbler", + // .... + "ContactRelations": [ { "@odata.etag": "W/\"JzQ0O0YvQnArR0ppdlR2MU0vNzNJWk9HV3pqeEdxSndxMnpOc0ZBc2orS3BpNGc9MTswMDsn\"", "No": "R02", @@ -278,86 +288,17 @@ After publishing `page5050.Contact Card` as web service ContactCard, it is possi } ``` -You can also use the asscociation within the containment, e.g. `ContactCard('KT200038')?$expand=ContactCardContact_Relations($expand=Relation_to_Contact_No_Link)` returns - -```js -{ - "@odata.context": "http://bc160:7048/BC/ODataV4/$metadata#Company('Cronus%20AG')/ContactCard/$entity", - "@odata.etag": "W/\"JzQ0O1FTc0xuRWpQcUMrU0t0VFJRWFFwZzJiNnBacEU3U3Jvd2dCRGVKam5pLzQ9MTswMDsn\"", - "No": "KT200038", - "Name": "Karen Berg", - //.. - "ContactCardContact_Relations": [ - { - "@odata.etag": "W/\"JzQ0O0YvQnArR0ppdlR2MU0vNzNJWk9HV3pqeEdxSndxMnpOc0ZBc2orS3BpNGc9MTswMDsn\"", - "No": "R02", - // ... - "Description": "reports to", - "Relation_to_Contact_No_Link": [ - { - "@odata.etag": "W/\"JzQ0O0c0VXhSbWNFTEhPU05WVWpNbVVEV0VITzFaaUxydXJMT3ZWdkJWWitYY1E9MTswMDsn\"", - "No": "KT200022", - "Name": "Lone Kuhlmann", - "Company_Name": "DanMøbler", - // .... - } - ] - }, - { - "@odata.etag": "W/\"JzQ0OzJHc0poang3ZGpIMkRmZGxubzVMOFlQM2ZXa1hmNzhDRHRZMjF4ZDJST2c9MTswMDsn\"", - "No": "R03", - // ... - "Description": "is partner of", - "Relation_to_Contact_No_Link": [ - { - "@odata.etag": "W/\"JzQ0O2F6eHFDSzhnRndrMVlMNHRUMy9mejYxZ0M5UElLQjM1REtHeHRETVVDakk9MTswMDsn\"", - "No": "KT200025", - "Name": "Ole Gotfred", - "Company_Name": "DanMøbler", - "Post_Code": "2100", - // .... - } - ] - } - ] -} -``` -Can we go one step deeper into the rabbit hole? Unfortunately not, because of the entity types: -* `ContactCard` offers containment for `ContactRelation` -* `ContactRelation` has an association to `Contact` (List) +Can we go one step deeper into the rabbit hole? Yes we can! -Poor Karen, it is still not possible to detect that Ole cheats here with Lone in a single request. +* `Contact` offers containment for `ContactRelation` +* `ContactRelation` has an association to `Contact` -Can we add the containment for `ContactRelation` to ``Contact` instead of `ContactCard`? -Lets give it a try: -```AL -pageextension 50106 "ContactRelationListExtension" extends "Contact List" -{ - layout - { - addafter(Control1) - { - group("ContactRelGroup") - { - part("Relations"; "Contact Relation Listpart") - { - SubPageLink = "Contact No." = FIELD("No."); - } - } - } - } -} -``` -Hoorray, this adds the following navigaton property: -```xml - ``` -The name of the property combines the name of the service and the name of the part. -The group `ContactRelGroup` prevents that the ContactRelation appears as list within UI as part of te contact list. -Lets give it a try `...Contact('KT200038')?$expand=ContactRelations($expand=Relation_to_Contact_No_Link($expand=ContactRelations))`: + +Lets give it a try and lets query `...Contact('KT200038')?$expand=ContactRelations($expand=Relation_to_Contact_No_Link($expand=ContactRelations))`: ```js { "@odata.context": "http://bc160:7048/BC/ODataV4/$metadata#Company('Cronus%20AG')/Contact/$entity",