Aggregators

Konfoo provides a general purpose aggregator framework for generating of all sorts of outputs based on the initial configuration.

Some common use-cases:

  • Bill of materials
  • Estimates (time, cost, etc)
  • Summaries
  • Work instructions

Aggregators are described as a set of rules that all can be applied to some Domain Entity in the configuration state.

Each aggregator accepts the following root level keys:

  • options - Options for the aggregator:
    • name - optional, default: null
    • path - required, used to access the aggregator results by 3rd parties
    • strip_empty - optional, enables the feature to strip rule outputs that are designated "empty"
  • meta - Provides options to send per-configuration metadata to 3rd party systems
  • rules - List of Aggregator Rules

To add the aggregator to your Konfoo project you must include it in the aggregators section in your main domain definition file:

options:
    aggregators:
        - bom.yml

The example below illustrates a bill of materials ruleset for calculating the amount of clay needed to make the Mug from previous chapters.

# General options for this aggregator
options:
    name: Bill of materials # optional, default: null

    path: bom # required
    # `path` is used in the output url: <konfoo-host>/agg/bom/<session>
    #    `------------------------------------------------ยดยดยด

    # Takes a list of rule output object fields
    # If the output object has any such field and
    # and the value on the field is () or 0 the rule's output is discarded
    strip_empty:
        - product_qty

# Metadata - usually to provide extra information to 3rd party software
meta:
    template_product: KONFOO-TEMPLATE
    name: |
        (expr)
        if root.fields.project_name != () {
            `MUG-{root.fields.height}-{root.fields.diameter}`
        } else { () }
    a_static_value: 42

# The actual aggregator rules
rules:
    - amount_of_clay:
        domain: Mug
        require:
            - self.height
            - self.diameter
        product_code: CLAY-001
        unit: g
        product_qty: |
            (expr) clay_calculations::calculate_mug_clay(self.height, self.diameter)

    - clay_for_handle:
        domain: Mug
        require:
            - self.has_handle
        product_code: CLAY-001
        unit: g
        product_qty: (expr) clay_calculations::calculate_handle_clay(self.height)

The above aggregator would then yield the following structure from the aggregator endpoint:
(e.g. by curl https://<konfoo-host>/agg/bom/<configuration-id>)

{
  "data": [
    {
      "__id__": "amount_of_clay",
      "product_code": "CLAY-001",
      "product_qty": 390,
      "unit": "g"
    },
    {
      "__id__": "clay_for_handle",
      "product_code": "CLAY-001",
      "product_qty": 78,
      "unit": "g"
    }
  ],
  "meta": {
    "product_name": "MUG-440-120",
    "a_static_value": 42
  },
  "name": "Bill of materials"
}

Aggregator rules

Rules for defining aggregator rules:

  • Every rule entry has a few reserved keywords:
    • domain - Domain Entity name this rule is for, defaults to the root Domain Entity
    • require - List of expressions that must evaluate to a "truthy" value for this rule to be evaluated
    • custom
      • Allows overriding the output of the rule by returning an Map object yourself
      • This does not require (expr) prefix
      • Any other fields besides the reserved ones are ignored when custom is present
  • Every other key in the rule object is exported to output
  • If a value for a key is prefixed by (expr) it is considered code and is evaluated when the aggregated result is requested
  • The evaluated expression is expected to return a value, but that is not enforced (null is OK)
  • Every rule in the rules list is executed sequentially
  • Every key in the rule is evaluated in order of declaration
  • Each rule is executed with it's own fresh scope - meaning variables defined in a previous key are available in the next
  • Rule is only executed if every expression in require evaluates to a "truthy" value
  • Rules are expanded for every instance of the Domain Entity set domain - meaning one rule could be executed multiple times if there are multiple instances of a Domain Entity in the configuration
  • Every rule has access to the same script modules and APIs as compute functions
  • The self constant refers to the specific Domain Entity instance the rule is currently being evaluated for

Let's look at one of the rules from the above example more closely:

rules:
    # This is the ID of the rule.
    # One rule could have multiple results in the aggregated object and this
    # provides a trace back to from which rule the object originated from.
    # This value is always present on the "__id__" field in the output.
    - amount_of_clay:
        domain: Mug # References the Domain Entity this rule deals with; default: root model

        # Here we require that `height` and `diameter` fields of Mug are set
        require:
            - self.height
            - self.diameter

        # These two are static string values
        product_code: CLAY-001
        unit: g

        # This value is calculated by a function in a script module
        product_qty: |
            (expr) clay_calculations::calculate_mug_clay(self.height, self.diameter)