# Extension Data Constraints



Overview [#overview]

*Extension Data Constraints* are a tool to help ensure that extension data fields that are related are persisted in valid combinations. For example, suppose you had a table of vehicle data like this:

**Vehicles**

| Make   | Model   | Year |
| ------ | ------- | ---- |
| Ford   | Mustang | 2020 |
| Ford   | Mustang | 2021 |
| Ford   | F150    | 2021 |
| Toyota | Camry   | 2020 |
| Toyota | Camry   | 2021 |
| Toyota | Camry   | 2022 |

In this case, if the user first chooses *Ford* as the Make, then you would want to present only *Mustang* and *F150* as options for Model, and not *Camry*. Likewise, if the user then selects *F150* for the Model, they should be constrained to select only *2021* as the Year, and not *2020*.

Extension Data Constraints support this sort of constraint building for user interfaces, and can also be used as a validation step to ensure that data created outside of the normal UI also conforms to the constraints desired (though this validation can be bypassed if needed.)

Structure [#structure]

Data Constraints may be simple (such as constraining the value of a field based on the value of another field on that same element), or complex, where the interdependent fields are on completely different elements within the policy structure. The components used are:

* **Configuration**, where you declare dependencies, using a path structure
* **Dependency Maps**, which are generated for segments based on the actual data in a segment
* **Filter Evaluation API**, which will compute allowed values for fields based on existing or prospective updated data
* **Constraint Tables**, which are a special kind of [table](/api/resources/tables#constraint_tables_api) used for defining valid combinations of data
* **Extended Validation**, which can ensure that data on the segment meets constraint requirements, regardless of whether it originated in a UI

Configuration [#configuration]

Each <ApiLink name="PropertyRef">property</ApiLink> in a data extension has a <ApiLink name="PropertyConstraint">constraint</ApiLink> field, which defines:

* The name of the `table` that contains the valid combinations
* The `column` of the table that has valid values for that particular field
* The `where` clause, which contains constraints for other columns in the table, or a static list of allowed values

For example, a configuration might include the following field declarations within a `vehicle` element declaration:

```json
{
	"vehicleMake": {
		"type": "string",
		"constraint": {
			"table": "vehicle_data",
			"column": "make_name"
		}
	},
	"vehicleModel": {
		"type": "string",
		"constraint": {
			"table": "vehicle_data",
			"column": "model_name",
			"where": {
				"make_name": {
					"key": "vehicleMake"
				}
			}
		}
	},
	"vehicleYear": {
		"table": "vehicle_data",
		"column": "year",
		"where": {
			"make_name": {
				"key": "vehicleMake"
			},
			"model_name": {
				"key": "vehicleModel"
			}
		}
	}
}
```

In this example, we declare that:

* The vehicle make must be one of the values in the `vehicle_data` table's `make_name` column.
* The model must be one of the values in the `model_name` column of that same table, but only including those rows that have `make_name` matching the make selected by the user.
* The year must be one of the values in the `year` column of the table, but only including those rows that have `make_name` matching the selected make *and* `model_name` matching the selected model.

Dependency Maps [#dependency-maps]

After a quote or policy transaction is created, the UI can call the <ApiLink name="fetchDependencyMapForQuote" /> or <ApiLink name="fetchDependencyMapForPolicyTransaction" /> endpoint. The result will be the *Dependency Map*, which describes the dependencies and constraints that apply for that quote or policy transaction.

A DependencyMap is a nested map that references <ApiLink name="ConstraintDependency" /> objects, each of which has the same structure as the constraints in configuration. The difference is that each of these is referenced by the actual locator of data for the quote or policy transaction. For example, a dependency map using the above configuration might look like this:

```json
{
  "01HTKMYZ0W7QT7VMSMMCFQNT1Q": {
    "vehicleMake": {
      "table": "vehicle_data",
      "column": "make_name"
    },
    "vehicleModel": {
      "table": "vehicle_data",
      "column": "model_name",
      "where": {
        "make_name": {
          "fieldName": "vehicleMake",
          "staticLocator": "01HTKMYZ0W7QT7VMSMMCFQNT1Q"
        }
    },
    "vehicleYear": {
      "table": "vehicle_data",
      "column": "year",
      "where": {
        "make_name": {
           "fieldName": "vehicleMake",
           "staticLocator": "01HTKMYZ0W7QT7VMSMMCFQNT1Q"
        },
        "model_name": {
          "fieldName": "vehicleModel",
          "staticLocator": "01HTKMYZ0W7QT7VMSMMCFQNT1Q"
        }
      }
    }
  }
}
```

This map indicates that:

* The only element that has constrained data is the vehicle, which has a `staticLocator` of `01HTKMYZ0W7QT7VMSMMCFQNT1Q`.
* The vehicle make is still dependant on the table data only, as declared in the configuration
* The vehicle model has the same table dependency but also only rows with a `make_name` that equal the `vehicleMake` property for that vehicle will be considered.
* The vehicle year is similar but will only match rows with a matching make name (like the model), but also where `model_name` matches the `vehicleModel` on the element.

Now, with the dependency map, the UI has enough information to ask Socotra what the *actual* allowed values should be for the make and model fields.

<Callout>
  When adding elements, the client should fetch a new dependency map if any of those elements could affect constraint processing in the UI.
</Callout>

Constraint Evaluation API [#constraint-evaluation-api]

The UI can use the dependency map, together with the values selected so far by the user, in order to get updates of what values are valid for remaining inputs. This is done by calling the <ApiLink name="evaluateConstraintsForQuote" /> or <ApiLink name="evaluateConstraintsForPolicyTransaction" /> endpoints.

For example, with the dependency map above, the UI could send this request:

```json
{
	/* as received earlier */
	"01HTKMYZ0W7QT7VMSMMCFQNT1Q": {
		"vehicleMake": {},
		"vehicleModel": {},
		"vehicleYear": {}
	},
	"01HTKN7Y5PSYF0D95RWTTZ88QT": {}
}
```

In this case, the user hasn't selected anything yet, and so the response is basic:

```json
{
  "01HTKMYZ0W7QT7VMSMMCFQNT1Q": {
    "vehicleMake": ["Toyota", "Ford"]
}
```

The system has determined that the `vehicleMake` options are fully determined since they only depend on table data, and there are no `where` restrictions, so all values found in the table are valid from the beginning. In contrast, the `vehicleModel` selection *does* depend on the `vehicleMake`, and since there are no rows with an empty `vehicleMake` value, there are no matches for the inputs, so there are no valid values returned.

Constraint Evaluation for Accounts [#constraint-evaluation-for-accounts]

Accounts do not require the specification of top-level element locators, so the call to the constraint evaluation is simpler. This difference is suggested by the request body formally specified in the [Tables API](/api/resources/tables), but the contrast can be illustrated in this simple example:

*Request:*

```json
{
	"make": {}
}
```

*Response:*

```json
{
	"01JKVQE9G9GG6Y1NY6VVXJ7W8X": {
		"make": ["Audi", "honda", "toyota"]
	}
}
```

<Callout>
  When there are no values returned for a particular field, the UI could be programmed to disable or hide those fields depending on the user experience they require.
</Callout>

<Callout type="warn">
  If a value is *not* included in the evaluation request, the system will defer to the currently persisted value of the element to fill those values. This behavior may change to treating those cases as having "no value" as described above.
</Callout>

After the user chooses a vehicle make, such as *Ford*, the UI can update the evaluation request as follows:

```json
{
	/* this hasn't changed from the earlier request */
	"01HTKMYZ0W7QT7VMSMMCFQNT1Q": {
		"vehicleModel": {
			"make_name": "Ford"
		}
	}
}
```

And the response is:

```json
{
	"vehicleMake": ["Toyota", "Ford"],
	"vehicleModel": ["Mustang", "F150"]
}
```

Or set the `data.vehicleMake` in the quote/transaction data extension, save and now the value is used:

```json
/* quote or transaction data extension field */
{
	"data": {
		"vehicleMake": "Toyota"
	}
}
```

```json
{
	/* quote/transaction: data.vehicleMake field value is automatically used */
	"01HTKMYZ0W7QT7VMSMMCFQNT1Q": {
		"vehicleMake": {},
		"vehicleModel": {}
	}
}
```

And the response is:

```json
{
	"vehicleMake": ["Toyota", "Ford"],
	"vehicleModel": ["Camry"]
}
```

The system determined that `vehicleModel` has a valid match with the input value from quote or transaction `Toyota`.

Referencing Other Elements [#referencing-other-elements]

When specifying the reference field that constrains another field, that reference field does not have to be in the same element. If only its name is included in the `key` property, it refers to that field on the same element. If prefixed with `..`, such as `"../driverPoints"`, the constraint comes from the `driverPoints` field on the *parent* element. These can be chained; for example, `../../driverPoints` refers to the `driverPoints` field on the parent's parent element.

The root (product) element can be referred to by prefixing a slash, such as `/driverPoints`.

Validation [#validation]

The discussion above refers to the up-front process used while the quote or policy transaction is still in `draft` state. When the user has made their selections, the transaction can proceed to the `validated` state. By default, the system will apply the constraints implied by the configuration and constraint tables to ensure that the actual data meets the constraints, even if the origin of the data was not the UI.

<Callout type="warn">
  The ability to bypass this validation is not yet present, but will be added in an upcoming release.
</Callout>
