ResourceSchema.fromJson constructor

ResourceSchema.fromJson(
  1. Map<String, dynamic> json,
  2. String path, {
  3. PanelDirection direction = PanelDirection.ltr,
  4. List<String> locales = const [],
})

direction is not read off json — a resource's direction always comes from its panel. It defaults to PanelDirection.ltr so a directly constructed resource (tests, the contract test, ResourceSchema.fake) keeps working unchanged; PanelSchema.fromJson passes the panel's parsed direction into every resource it builds. locales follows the same rule, for the same reason: panel.locales is a panel-level fact, not a per-resource one, so it is propagated here rather than read off each resource's own JSON (which carries none).

Implementation

factory ResourceSchema.fromJson(
  Map<String, dynamic> json,
  String path, {
  PanelDirection direction = PanelDirection.ltr,
  List<String> locales = const [],
}) {
  final sortNodes = objects(json, 'sorts', path);
  final rawGroup = opt<String>(json, 'group');

  return ResourceSchema(
    key: req<String>(json, 'key', path),
    labels: ResourceLabels.fromJson(
      req<Map<String, dynamic>>(json, 'labels', path),
      '$path.labels',
    ),
    // object(), not opt(): an absent sub-object takes the documented default,
    // but one that is present and not an object is a contract violation.
    // Reading it as absent would silently deny every permission, blank the
    // card, or switch search off.
    permissions: ResourcePermissions.fromJson(
      object(json, 'permissions', path) ?? const {},
    ),
    recordKey: opt<String>(json, 'recordKey') ?? 'id',
    card: CardLayout.fromJson(
      object(json, 'card', path) ?? const {},
      '$path.card',
    ),
    search: ResourceSearch.fromJson(object(json, 'search', path) ?? const {}),
    sorts: List.generate(
      sortNodes.length,
      (index) =>
          ResourceSort.fromJson(sortNodes[index], '$path.sorts[$index]'),
    ),
    filters: SchemaComponent.listFromJson(json, 'filters', path),
    form: SchemaComponent.listFromJson(json, 'form', path),
    infolist: SchemaComponent.listFromJson(json, 'infolist', path),
    // Same rule as [direction] on this resource itself: propagated at parse
    // time, not read off each relation's own JSON (which carries none).
    relations: RelationDescriptor.listFromJson(
      json,
      path,
      direction: direction,
    ),
    group: rawGroup?.isEmpty ?? false ? null : rawGroup,
    badge: switch (object(json, 'badge', path)) {
      final badge? => ResourceBadge.fromJson(badge),
      null => null,
    },
    direction: direction,
    locales: locales,
  );
}