CodamAIDocs
Topicdone

Reading through abstract types

If a reference points to an abstract model, CDMS returns the fields of all possible subtypes. This page explains how that is resolved and how the client tells the types apart.

Variants
abstract single referenceabstract list@type in the response+ means: fields of all subtypesfield of only one subtypepermissions of the subtype

What this is about

An abstract model is an umbrella term with several concrete subtypes, for example kunde (customer) with privatkunde (private customer) and firmenkunde (business customer). How this works in general is described under Abstract models and @type.

This page covers the case where a reference points to an abstract model. An order belongs to a customer, and this customer can be a private customer or a business customer. When expanding, CDMS does not know in advance which subtype will come.

The example model

classDiagram
    class auftrag {
        nummer
        kunde
    }
    class berater {
        name
        kunden
    }
    class kunde {
        <<abstract>>
        name
    }
    class privatkunde {
        geburtsdatum
    }
    class firmenkunde {
        handelsregisternummer
    }
    auftrag --> kunde : kunde (single)
    berater --> kunde : kunden (list)
    kunde <|-- privatkunde
    kunde <|-- firmenkunde

auftrag is an order, berater an advisor. geburtsdatum is the date of birth, handelsregisternummer the commercial register number.

@typeFields
crm.privatkundename, geburtsdatum
crm.firmenkundename, handelsregisternummer

How CDMS resolves the response

While resolving the response, CDMS sees that kunde is abstract. It applies your inner response to each subtype and combines the results:

flowchart TB
    R["{ field: kunde, response: ['+'] }"] --> P["for privatkunde:<br/>name, geburtsdatum"]
    R --> F["for firmenkunde:<br/>name, handelsregisternummer"]
    P --> U["Union:<br/>name, geburtsdatum, handelsregisternummer"]
    F --> U
    U --> L["Read each object through its subtype:<br/>fields the subtype does not have are dropped"]

So a + in an abstract reference means “all simple fields of all possible subtypes”. When a private customer is read, name and geburtsdatum remain; for a business customer, name and handelsregisternummer.

Single reference and list

Expanding abstract references

When: auftrag.kunde with { "field": "kunde", "response": ["+"] }

  1. 1
    CDMS→Database
    reads the auftrag and attaches kunde with a LEFT JOIN, taking only the id and the stored type
    The type comes from the _MODELTYPE column of the parent model.
  2. 2
    CDMS
    picks the subtype from the type, e.g. privatkunde
  3. 3
    CDMS
    checks the read role of this subtype
  4. 4
    CDMS→Database
    reads the customer through the subtype, with its row filters
  5. 5
    CDMS
    puts it with @type into the field kunde

Result: "kunde": { "@type": "crm.privatkunde", "name": …, "geburtsdatum": … }

When: berater.kunden with { "field": "kunden", "response": ["+"], "parameter": { … } }

  1. 1
    CDMS→Database
    Phase 1: searches the parent model kunde for id and type only, with the filter on the advisor, your filter, your sorting and your limit
  2. 2
    CDMS
    splits the matches by subtype
  3. 3
    CDMS→Database
    Phase 2: reads all matches of each subtype at once, through its permissions and filters
  4. 4
    CDMS
    assembles the list in the order from phase 1, every object with @type

Result: A mixed list of private and business customers, sorted correctly.

When: { "field": "kunde", "response": ["name", "geburtsdatum"] }, but the customer is a business customer.

A business customer has no geburtsdatum. CDMS silently leaves it out for this object, without an error.

Result: 200 with name, geburtsdatum is missing for this object.

When: The user may read private customers, but not business customers.

Permissions apply per subtype. If the reference points to a business customer, the request fails with 403 and the role of the business customer. If it points to a private customer, it works.

Result: Whether the request works depends on which subtype is in the data.

The result in the example

An advisor with a mixed customer list
Request
POST /api/rest/crm/berater/read/b5…
{
  "response": [
    "name",
    {
      "field": "kunden",
      "response": ["+"],
      "parameter": { "order": [{ "field": "name", "order": "ASC" }] }
    }
  ]
}
Response
{
  "data": {
    "id": "b5…",
    "name": "Petra Berger",
    "kunden": [
      { "@type": "crm.privatkunde", "id": "5a2b…",
        "name": "Anna Muster", "geburtsdatum": "1990-04-01" },
      { "@type": "crm.firmenkunde", "id": "7c1d…",
        "name": "Muster GmbH", "handelsregisternummer": "HRB 12345" }
    ]
  },
  "meta": { "error": false }
}

Evaluating @type in the client

@type is the API path of the subtype with dots instead of slashes. Branch on it in the client, not on which fields are filled:

for (const kunde of berater.kunden) {
  switch (kunde["@type"]) {
    case "crm.privatkunde":
      showPrivateCustomer(kunde);
      break;
    case "crm.firmenkunde":
      showBusinessCustomer(kunde);
      break;
  }
}

Decision table

Reference kunde with response ["name", "geburtsdatum"]
stored typeread role of this subtypeContent of kunde
crm.privatkundeyesname, geburtsdatum, @type
crm.firmenkundeyesname, @type; geburtsdatum is dropped
–no403 with the role of the subtype
no reference set–null

Pitfalls

Where to go next

Sources in the code and the knowledge base
  • CDMS/cdms-rest-api – Expander.expandModelResponse (union over all subtypes)
  • CDMS/cdms-system-layer – AbstractLayer.fetchAndSetModel, fetchAndSetList, queryAbstract
  • CDMS/cdms-persistence-database – SelectionBuilder.resolveJoinTypeExpression
  • CDMS/cdms-integrationtest – AbstractPolymorphicReferenceTest, AbstractResponseTest.simpleReadAbstractWithModelType
  • documentation/20-api/03-response-requests.md
Search