Mapping sur plusieurs niveaux

Bonjour,

Je viens vers vous sur au sujet du mapping mis en place spécifiquement pour nous.
Mon besoin est de gérer des liens entre business object dans le mapping. je souhaite pouvoir avoir accès au contenu via une autre resource qui lui ai lié.
Je sais qu’on peut le faire sur “un niveau” (entre deux objets) grâce à la méthode “addRefField”.
Je souhaiterai savoir si il est possible de le faire sur plus de “un niveau”. J’ai trois objets A, B et C. A est lié à B et B est lié à C. Je sais donc déjà accéder à B via A en utilisant un “addRefField”.
Mon besoin est de pouvoir accéder à C via le mapping de A.
J’ai vu quelques exemples dans des codes de mon appli mais via un appel Postman, je ne vois pas le champ de C recherché et j’ai une erreur dans les logs.

J’espère que c’est suffisamment clair…

Merci

Non ce cas d’embedding a plusieurs niveau n’est, je pense, pas géré en l’état par le composant de mapping de webservices RESTMappedObjectsExternalObject.

J’ai classé votre demande en feature request en attendant de regarder cela de plus près.

PS: ce genre de chose existe en standard dans les APIs génériques des treeviews, le composant de mapping qui a été créé pour vous car les APIs génériques avaient été jugées “trop compliquées” convergent petit à petit vers ce que savent faire out of the box les APIs génériques dont la “complexité” n’est pas le fruit du hasard…

Très bien.

Est-il au moins possible de mapper la foreign key de C présente dans B comme un champ “classique”? A l’aide d’un addField(“fk-b.fk-c”) par exemple?
Je ne sais aps si c’est très clair…

Non ce n’est pas très clair. Tout dépend de la direction du 1-N entre vos objets.

si A --(1-N)–< B --(1-N)–< C il est possible sur un record C d’intégrer les attributs ramenés du record B auquel il est relié ainsi que les attributs du record A auquel ce record B est relié (ce système de remontée d’attributs via les relations N-1 est un mécanisme basique de la plateforme Simplicité).

Si votre besoin est d’avoir sur un record A la liste des records B liés puis au sein de chaque record B la liste des records C liés à ce record B c’est là où il y a une limitation actuellement avec les APIs mappées car elles ne permettent d’embedder des listes de records liés qu’à 1 seul niveau pour le moment (i.e. un A avec la liste des B liés ou un B avec la liste des C liés, mais pas un A avec la liste des B liés contenant chacun la liste des C liés).

Comme je le dit plus il n’y aurait pas cette limitation avec les APIs génériques qui savent exposer des “grappes” de records à N niveaux via le mécanismes générique des treeviews qui est fait pour ce genre de choses mais qui n’a pas été transposé sur les APIs mappées car cela n’a pas été demandé jusqu’ici d’où la qualification en “feature request” de ce post.

NB: Par le mécanisme générique des liens virtuels entre objets il serait possible dans la config ci-dessus de lier virtuellement à A l’union des records de C indirectement liés à A via B. Dans ce cas on pourrait avoir un A avec la liste des B liés + la liste (distincte) des C liées indirectement via les B mais on ne pourrait pas savoir quel(s) B relie(nt) indirectement un C au A), je n’ai pas proposé cette mécanisme car je pense que ce n’est pas votre besoin mais je me trompe peut être…

Bonjour
d’après votre réponse,

limitation actuellement avec les APIs mappées car elles ne permettent d’embedder des listes de records liés qu’à 1 seul niveau pour le moment (i.e. un A avec la liste des B liés ou un B avec la liste des C liés, mais pas un A avec la liste des B liés contenant chacun la liste des C liés).

il est donc possible d’exposer un objet A qui aurait un attribut B de la façon suivante :

{
	idA : xxx
	nameA : xxx
	monObjetB: {
		idB : xxx
		nameB : xxx
	}
}

Pouvez-vous m’indiquer comme faire ce niveau 2 ?
Actuellement je suis obligée d’aplatir mon objet B et remonter tous les attributs de B, comme ceci

{
	idA : xxx
	nameA : xxx
	idB : xxx
	nameB : xxx
}

Version
[Platform]
Status=OK
Version=4.0.P24
BuiltOn=2020-02-05 10:36 (revision 4271ab3e8ebfa613335f5cb919c3f3d2077b7a28)
Encoding=UTF-8
EndpointIP=172.17.0.5
EndpointURL=http://1271acf15d72:8080
TimeZone=Europe/Paris
SystemDate=2020-02-05 16:29:53

Merci pour votre retour

J’ai l’impression qu’il y a confusion sur le sens des relations dont on parle…

Considérons [A]--1--n--<[B] = un record de A est lié à n records de B.

  • Dans un sens on peut embedder dans un record A la liste des records B qui lui sont liés.
  • Dans l’autre sens un record B référence un et un seul record A, on ne parle pas d’embeder ici mais simplement d’avoir, sur un record B, les attributs ramenés depuis le record A auquel il est lié

Prenons un exemple concret sur la démo où on a [Supplier]--1--n--<[Product]

Avec le code de mapping suivant:

addObject("suppliers", "DemoSupplier");
addField("suppliers", "code", "demoSupCode");
addField("suppliers", "name", "demoSupName");

addObject("products", "DemoProduct");
// **Without** embedded list
//addRefField("products", "suppliers", "supplierId", "demoPrdSupId", "Reference to supplier's row ID");
// **With** embedded list
addRefField("products", "suppliers", "supplierId", "demoPrdSupId", "supplierProducts", true, "Reference to supplier's row ID");
addField("products", "supplierCode", "demoPrdSupId.demoSupCode");
addField("products", "supplierName", "demoPrdSupId.demoSupName");
addField("products", "reference", "demoPrdReference");
addField("products", "name", "demoPrdName");

On a pour un record Product une réponse du genre:

  1. en mode embedded list:
{
  "row_id": "1",
  "code": "BIM",
  "name": "BIM Computers Ldt",
  "supplierProducts": [
    {
      "supplierName": "BIM Computers Ldt",
      "reference": "REF003",
      "supplierId": "1",
      "name": "Infinite laptop",
      "supplierCode": "BIM",
      "row_id": "1"
    },
    {
      "supplierName": "BIM Computers Ldt",
      "reference": "REF005",
      "supplierId": "1",
      "name": "Wide curved screen",
      "supplierCode": "BIM",
      "row_id": "2"
    }
  ]
}
  1. en mode non embedded list :
{
  "code": "BIM",
  "_links": [{
    "rel": "supplierProducts",
    "href": "suppliers/1/supplierProducts"
  }],
  "name": "BIM Computers Ldt",
  "row_id": "1"
}

Par contre dans les 2 cas, pour un record Product on a toujours une réponse du genre:

{
  "supplierName": "BIM Computers Ldt",
  "reference": "REF003",
  "supplierId": "1",
  "name": "Infinite laptop",
  "supplierCode": "BIM",
  "row_id": "1"
}

Où les attributs du Supplier sont simplement ramenés (i.e. intégrés) dans le record du Product

Le sujet de ce feature request est de pouvoir embedder des listes à plusieurs niveaux à la manière d’un treeview, ex: pour [Supplier]--1--n--<[Product]--0--n--<[Order] on aurait

{
  "row_id": "1",
  "code": "BIM",
  "name": "BIM Computers Ldt",
  "supplierProducts": [
    {
      "supplierName": "BIM Computers Ldt",
      "reference": "REF003",
      "supplierId": "1",
      "name": "Infinite laptop",
      "supplierCode": "BIM",
      "row_id": "1",
      "productOrders": [
        {
           (order data)
        },
        (...)
      ]
    },
    {
      "supplierName": "BIM Computers Ldt",
      "reference": "REF005",
      "supplierId": "1",
      "name": "Wide curved screen",
      "supplierCode": "BIM",
      "row_id": "2",
      "productOrders": [
        {
           (order data)
        },
        (...)
      ]
    }
  ]
}

Ce que vous demandez n’a rien à voir avec ce feature request car si je comprend bien vous voudriez au lieu de:

{
  "supplierName": "BIM Computers Ldt",
  "reference": "REF003",
  "supplierId": "1",
  "name": "Infinite laptop",
  "supplierCode": "BIM",
  "row_id": "1"
}

plutôt ça:

{
  "reference": "REF003",
  "name": "Infinite laptop",
  "row_id": "1"
  "supplier": {
    "row_id": "1",
    "name": "BIM Computers Ldt",
    "code": "BIM",
  }
}

Ce besoin n’a jamais été exprimé et n’est pas le sujet de ce feature request.

Si ça correspond à un besoin (compatible avec vos normes maison de structuration des APIs) alors dans ce cas postez une autre feature request ad hoc.

Pour en revenir au sujet initial je viens de tester sur une version “alpha” un mapping d’embedded lists à 2 niveaux et ça me semble marcher très bien:

{
  "supplierProducts": [
    {
      "supplierName": "BIM Computers Ldt",
      "reference": "REF003",
      "supplierId": "1",
      "name": "Infinite laptop",
      "supplierCode": "BIM",
      "row_id": "1",
      "productOrders": [
        {
          "number": "131",
          "productId": "1",
          "row_id": "131"
        },
        {
          "number": "130",
          "productId": "1",
          "row_id": "130"
        }
      ]
    },
    {
      "supplierName": "BIM Computers Ldt",
      "reference": "REF005",
      "supplierId": "1",
      "name": "Wide curved screen",
      "supplierCode": "BIM",
      "row_id": "2",
      "productOrders": [
        {
          "number": "90",
          "productId": "2",
          "row_id": "90"
        },
        {
          "number": "88",
          "productId": "2",
          "row_id": "88"
        },
        {
          "number": "82",
          "productId": "2",
          "row_id": "82"
        }
      ]
    }
  ],
  "code": "BIM",
  "name": "BIM Computers Ldt",
  "row_id": "1"
}

Avec ce code de mapping:

		addObject("suppliers", "DemoSupplier");
		addField("suppliers", "code", "demoSupCode");
		addField("suppliers", "name", "demoSupName");

		addObject("products", "DemoProduct");
		addRefField("products", "suppliers", "supplierId", "demoPrdSupId", "supplierProducts", true, "Reference to supplier's row ID");
		addField("products", "supplierCode", "demoPrdSupId.demoSupCode");
		addField("products", "supplierName", "demoPrdSupId.demoSupName");
		addField("products", "reference", "demoPrdReference");
		addField("products", "name", "demoPrdName");

		addObject("orders", "DemoOrder");
		addRefField("orders", "products", "productId", "demoOrdPrdId", "productOrders", true, "Reference to product's row ID");
		addField("orders", "number", "demoOrdNumber");

Du coup @ThomasB je ne comprend pas l’objet de cette feature request… Aviez vous essayé de le faire avant de poster cette demande d’évolution ?

Bonjour
désolée pour le dérangement, mon besoin venait de consignes contradictoires entre le valideur de mon api (qui voulait une structuration sur plusieurs niveaux) et les normes de mon entreprise (qui exigent des structures à plat). Veuillez donc ne pas tenir compte de mon précédent message.
Merci toutefois pour votre retour

C’est bien ce qui me semblait… Pas de pb.

Pour mémoire le composant de mapping d’API a été implémenté pour vous pour se caler sur vos normes. Toute demande d’évolution portant sur ce composant doit donc d’abord être validées vis à vis de ces normes.

Par contre @ThomasB, je veux bien savoir si le besoin initial de ce post (a savoir des listes embedded à plusieurs niveaux) fonctionne aussi dans votre cas comme ça fonctionne dans le cas que j’ai décrit précédemment.

Ca me permettrait de requalifier ce post en “support” plutôt qu’en “feature request”

Merci

Effectivement ça fonctionne.

Bonjour David,
je prends la main sur le sujet.

En effet, Simplicité est de fait conforme au design guide API en vigueur à l’heure actuelle.
Cependant, nos Urbanistes des API travaillent (si si) sur l’élévation de nos pratiques pour que nous puissions accéder au graal de l’API tout en alignant l’ensemble des providers de ressources (que nous sommes) sur la sémantique d’Entreprise (notre fameux MIM). Comme nous serons parmi les premiers en interne à atteindre cette cible, autant orienter les spécifications du nouveau design guide.

Nous souhaitons maintenir la possibilité de servir les propriétés “à plat” dans la plupart des cas (fonctionnement actuel). Ce comportement est explicitement paramétré dans les mappings. Cela convient parfaitement.

Dans d’autres cas, nous souhaitons exploiter les paramètres des relations (0-n ou n-n) entre objets - à nouveau de manière spécifiquement activable comme c’est le cas actuellement via le flag booléen de addRefField.

Afin d’illustrer la suite, considérons cette partie du modèle:

image

  • Objet métier Company (hérite de Organization)
  • Objet métier Structural unit
  • Une Structural unit -belongs to-1 Company
  • Une Company -has-n- Structural units

Je laisse de côté pour l’instant la relation réflexive car c’est un cas qui n’est pas encore implémenté dans mon modèle BCSI

  • Une Structural unit -is part of-n Structural unit
  • Une Structural unit -has part-n Structural unit

Mapping:
addObject(“companies”, “BCSILegalEntity”, “Renault group legal entities (companies).”)
addObject(“structural-units”, “BCSILegalEntityDep”, “Renault group legal entities structural-units (departments).”)

actuellement / mode non embedded addRefField(“structural-units”, “companies”, “belongs-to”, “DepLegalEntityId”, “has”, false, “Structural-units (department) structuring the legal entity (organization).”)

Get Company

{
  "corporateName": "ALD AUTOMATIVE",
  "_links": [
    {
      "rel": "has",
      "href": "companies/2/has"
    }
  ],
  "row_id": "2",
  "rowId": "2"
}

Get Structural unit (champ d’objet Company.corporateName explicitement mappé “à plat” dans Structural unit)

{
  "corporateName": "ALD AUTOMATIVE",
  "belongs-to": "2",
  "label": "Achats",
  "row_id": "1",
  "rowId": "1"
}
  • actuellement / mode embedded addRefField(“structural-units”, “companies”, “belongs-to”, “DepLegalEntityId”, “has”, true, “Structural-units (department) structuring the legal entity (organization).”)
{
  "corporateName": "ALD AUTOMATIVE",
  "has": [
    {
      "corporateName": "ALD AUTOMATIVE",
      "belongs-to": "2",
      "label": "Achats",
      "row_id": "1",
      "rowId": "1"
    },
    {
      "corporateName": "ALD AUTOMATIVE",
      "belongs-to": "2",
      "label": "ACO",
      "row_id": "2",
      "rowId": "2"
    },
    {
      "corporateName": "ALD AUTOMATIVE",
      "belongs-to": "2",
      "label": "Agence RH",
      "row_id": "6",
      "rowId": "6"
    }
  ],
  "row_id": "2",
  "rowId": "2"
}

Première évolution demandée:

  • En mode embedded, la patte ‘n’ de la relation est effectivement imbriquée mais la valeur de la clé “has” est un tableau de structural-units, il faudrait que ce tableau de structural-units soit imbriqué dans un objet : “has”: { “structural-units”: […] } au lieu de “has”:[…]
{
  "corporateName": "ALD AUTOMATIVE",
  "has": {
    "structural-units": [
      {
        "corporateName": "ALD AUTOMATIVE",
        "belongs-to": "2",
        "label": "Achats",
        "row_id": "1",
        "rowId": "1"
      },
      ...
    ]
  },
  "row_id": "2",
  "rowId": "2"
}

Deuxième évolution demandée :

  • En mode embedded, la patte ‘1’ de la relation n’est pas imbriquée, la seule option disponible est de mapper à plat, nous souhaitons pouvoir substituer aux champs actuellement mappés “à plat” un bloc contenant les propriétés mappés sur l’objet lié (companies) sans ses links et idéalement uniquement celles reportées comme object field de structural-units (ici, corporateName)

Get Structural unit (champ d’objet Company.corporateName ne serait plus mappé “à plat” dans Structural unit mais imbriqué dans le bloc “belongs-to”)

{
  "belongs-to": {
    "companies": [
      {
        "corporateName": "ALD AUTOMATIVE",
        "row_id": "2",
        "rowId": "2"
      }
    ]
  },
  "label": "Achats",
  "row_id": "1",
  "rowId": "1"
}

Troisième évolution demandée :

  • En mode non embedded, la patte ‘1’ de la relation n’est pas reportée dans les links.

Get Structural unit (champ d’objet Company.corporateName reste ici explicitement mappé “à plat” dans Structural unit mais pourrait ne plus l’être)

{
  "_links": [
    {
      "rel": "belongs-to",
      "href": "structural-units/1/belongs-to"
    }
  ],
  "corporateName": "ALD AUTOMATIVE",
  "belongs-to": "2",
  "label": "Achats",
  "row_id": "1",
  "rowId": "1"
}

OK je mets ces évolutions dans la pile.

Cela dit le cas “has” et “belongs-to” me semblent plutôt (ou aussi) destinés à gérer les N-N.

En effet dans le cas 1-N le “has” est effectivement une liste avec N items mais le “belongs-to” ne peut être qu’une liste avec 1 seul item. Si on se limite à ce cas 1-N ça n’a donc pas d’intérêt que ça soit une liste.

Il va donc sans doute aussi savoir mapper un objet de relation N-N pour que ça se présente comme des listes “has” et “belongs-to”. Sachant que dans le cas d’une N-N (contrairement à la 1-N) la sémantique “a des” (“has”) et “appartient à” (“belongs-to”) est une question subjective de point de vue…

PS: on est bien d’accord que la mise en place de cette logique à base de “has” et “belongs-to” remplace ce qu’il y avait avant (je ne voudrais pas avoir à gérer, en plus, la compatibilité avec l’ancienne syntaxe)

Merci pour te retour rapide…

Attention; “has” et “belongs-to” sont des exemples bien évidemment. Ce sont des strings passées en paramètre des addRefField. Le principe est de pouvoir expliciter la signification et les cibles des relations.

addRefField(“structural-units”, “companies”, “belongs-to”, “DepLegalEntityId”, “has”, false, “Structural-units (department) structuring the legal entity (organization).”)

Et oui, effectivement, pour standardiser, je propose d’uniformiser le type JSON des “pattes” de relation en considérant toujours le pattern “relation name”: { “relation target name”: [<liste des occurrences liées>] } sachant que selon les cas (0-n ou n-n) la liste des occurrences peut être vide, ne contenir qu’un seul élément ou plusieurs.

PS: on est bien d’accord que la mise en place de cette logique à base de “has” et “belongs-to” remplace ce qu’il y avait avant (je ne voudrais pas avoir à gérer, en plus, la compatibilité avec l’ancienne syntaxe)

Ta remarque m’inquiète…

Le premier point consiste - si la relation est en mode imbriqué - à encapsuler la liste dans le bloc “nommé” de l’objet cible ( “has”: { “structural-units”: […] } au lieu de “has”:[…]). A priori, les modes imbriqués ne sont pas encore déployés/utilisés donc personne ne devrait être impacté. Donc pour ce premier cas, oui, on remplace…

Le deuxième point consiste - si la relation est en mode imbriqué - à substituer au row_id de l’objet lié un bloc “nommé” de l’objet cible ( “belongs-to”: { “companies”: [{…,row_id:2}] } au lieu de “belongs-to”:2). Donc pour ce deuxième cas, cf. § précédent, oui, on remplace…

Le troisième point (la patte ‘1’ de la relation n’est pas reportée dans les links) consiste à ajouter un bloc _links là où il n’y en avait pas donc ça ne remplace rien.