Skip to content
This repository has been archived by the owner on Apr 13, 2019. It is now read-only.

required

Konstantin Sobolev edited this page Jun 7, 2017 · 7 revisions

"Required" flag on projections

Most projection types support "required" flag (+) on fields and tags; in addition map keys in operation projections may be marked as required. This page summarizes different usages and semantics of this flag.

Before discussing what does it mean for something to be required let's see how Epigraph data is structured.

(Entity)Data is essentially a map from tags to their Values, where Value holds either an ErrorValue or a Datum. This means that every tag may be in one of the following states:

  • Absent
  • Contains a Value with ErrorValue
  • Contains a Value with (both ErrorValue and) Datum set to null ( NullValue)
  • Contains a Value with non-null Datum

Datum is a parent for (instances of) all the other types: maps, lists, records, primitives, and enums.

RecordDatum is a map from fields to Data instances. If field type is a Datum type, then Data instance will have only one special self tag containing Datum instance. This leads to the following options for a field:

  • Absent
  • is of Data type and contains arbitrary Data instance
  • is of Datum type and self tag contains ErrorValue
  • is of Datum type and self tag contains NullValue
  • is of Datum type and self tag contains non-null Datum instance

This implies that Datum field can be set to null (self tag contains NullValue), but Data fields can not: setting them to null removes the field. It goes in line with the philosophy of the Data type as null value wouldn't have any meaning for it.

Map and list values are structured the same way as field values.

Operation projections

Input and Update projections

Fields and tags may be marked as required, for instance:

  create {
    inputType list[BookRecord]
    inputProjection *(
      +title,                        // title required
      +author:+id,                   // author id required
      +text:+plain                   // text required
    )
  }

Required on Data tag mean that

  • tag must be present
  • it must not be an error
  • its value must not be null

Required on a field mean that

  • field must be present
  • if it's a DatumType then it must not be a null or an error (i.e. self tag is required)

Required map keys mean that map keys must be provided, * is not accepted in request projection.

Delete and Output projections

Operation delete and output projections don't have 'required' on fields and tags (although delete projection uses + on data projections to mark deletable entities), but they can still mark map keys as required with the same meaning as input and update projections, i.e. to force clients to pass map keys in the request.

Request projections

'Required' only applies to request output projections and enforces operation to somehow fulfill its contract: required but missing field is an operation error.

'Required' can be put on Data tags and RecordDatum fields. Guarantees provided to response consumers by required are the same as for the operation projections: data must be present, it must not be an error or null. Constraints are a bit more relaxed for operation implementations: errors and null values are allowed, and framework will try to get rid of them by sweeping them under the carpet data tree pruning.

Object State Action
required tag missing operation error, fail request
required tag set to error remove Data (field, map or list entry)
optional self tag set to error remove Data (field, map or list entry)
optional tag * keep as is
required field missing operation error, fail request
required field set to error replace whole record with error
required field set to null replace whole record with error (412)
optional field * keep as is
required key missing operation error, fail request
required key set to error replace whole map with error
required key set to null replace whole map with error (412)
optional key * keep as is
list entry * keep as is

Rules are applied recursively, bottom to top. This means that nulls and errors will bubble up until non-required field or tag is found and will become its value. If they bubble all the way up to the root then request fails.

More technically, this can be described as two functions:

  • pruneData(data, projection) -> Keep | Fail | Replace(newData) | Remove
  • pruneDatum(datum, projection) -> Keep | Fail | Replace(newDatum) | UseError(error)

with the following logic (first match wins)

Object State Pruning result Action
required tag/field/key missing return Fail
required tag set to error return Remove
required tag set to null return Remove
tag Replace replace
required or $self tag UseError return Remove
tag UseError replace with error
required field/key datum set to error return UseError
required field/key datum set to null return UseError(412)
required field/key Remove return UseError(412)
field/key/list item Replace replace
field/key Remove remove
list item Remove remove item (changes indices!)
* Keep keep
* Fail fail request