RelationDescriptor.fromJson constructor

RelationDescriptor.fromJson(
  1. Map<String, dynamic> json,
  2. String path, {
  3. PanelDirection direction = PanelDirection.ltr,
})

direction is not read off json — same rule as ResourceSchema.direction: a relation always inherits its owning resource's direction, propagated by ResourceSchema.fromJson through listFromJson below, one level deeper than PanelSchema.fromJson propagates it into the resource itself.

Implementation

factory RelationDescriptor.fromJson(
  Map<String, dynamic> json,
  String path, {
  PanelDirection direction = PanelDirection.ltr,
}) {
  // Required fields first, so a relation missing both its key and its card
  // is still reported as the missing key it is.
  final key = req<String>(json, 'key', path);
  final label = req<String>(json, 'label', path);
  final sortNodes = objects(json, 'sorts', path);

  final card = CardLayout.fromJson(
    object(json, 'card', path) ?? const {},
    '$path.card',
  );

  // A card with no slot at all renders zero widgets, so the section would
  // be a heading over nothing — the "disabled corpse" this contract does
  // not publish. Every other missing required field already drops the
  // relation; an absent, null or `{}` card used to be the one that did
  // not, and [listFromJson]'s own doc has always claimed otherwise. The
  // server refuses to publish one (`RelationDiscovery` names it in
  // `doctor`), and this is the client half of the same rule: a wire shape
  // nothing can render is a malformed relation, not a blank one.
  if (card == const CardLayout.empty()) {
    throw SchemaFormatException(
      '$path.card',
      'declares no slot, so the relation would render nothing',
    );
  }

  return RelationDescriptor(
    key: key,
    label: label,
    card: card,
    // Absent on an older server that predates this field — 'id' is the
    // same default `ResourceSchema.recordKey` takes, and the common case.
    recordKey: opt<String>(json, 'recordKey') ?? 'id',
    // Absent or null on a server predating P9, or one whose relation's
    // child model resolves to zero or several mobile resources: either way
    // the relation is read-only on this API. `opt` reads a wrong-typed
    // value as absent too — a client never invents a capability the server
    // did not declare.
    resource: opt<String>(json, 'resource'),
    // Absent on a server predating P11 reads as disabled/empty — the same
    // absence rule the `relations` array itself carries — while a present
    // but wrong-typed node throws, the convention ResourceSchema's own
    // `search`/`sorts` blocks already follow (object()/objects(), not opt).
    search: ResourceSearch.fromJson(object(json, 'search', path) ?? const {}),
    sorts: List.generate(
      sortNodes.length,
      (index) =>
          ResourceSort.fromJson(sortNodes[index], '$path.sorts[$index]'),
    ),
    direction: direction,
  );
}