# Defensive Data Consumption

## Data is Subject to Change

It is best to assume that all data outside of the substantial number of fields defined in the [OpenActive Modelling Specification](https://www.openactive.io/modelling-opportunity-data/) and [imin namespace](https://ns.imin.co/) is subject to change at any time. For more information on the rationale for this, see [Namespaces and Deprecation](https://docs.imin.co/using-the-platform/our-data/context-and-field-names).

This guide details best practices to help you code defensively to allow for such changes.

{% hint style="warning" %}
If you are considering using one of the unstable fields to drive critical production functionality, please get in touch with us at <hello@imin.co> and we will work with you to propose your use case to the [OpenActive W3C Community Group](http://w3c.openactive.io/), and move the field into either the OpenActive or imin namespace.
{% endhint %}

## Gracefully Handling Missing Data

Regardless of whether or not a property is a required field in the [OpenActive Modelling Specification](https://www.openactive.io/modelling-opportunity-data/), or whether it is assumed to be present in all cases (e.g. `"imin:fullAddress"`), it should be assumed that there will be an item of data returned where this property is missing or very short (e.g. only "N4 3FL").

### Adaptive User Interfaces

Panels (e.g. `<div>`) used to display properties within user interfaces should collapse if the value of the property is not present.

For example, the "time" icon in the image below should be hidden if the duration field is not present.

![](https://809863394-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LJbuIOE-TIcNUDnkUS7%2F-LMCVxLR6sL_9pBXgolT%2F-LMCVyAbIZCUibdnVszA%2Fscreen-shot-2018-09-11-at-00.47.28%20\(1\).png?generation=1536751302337525\&alt=media)

For another example, the design should anticipate a long and short `imin:fullAddress`.

![imin:fullAddress could be very long](https://809863394-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LJbuIOE-TIcNUDnkUS7%2F-LMCVxLR6sL_9pBXgolT%2F-LMCVyAdEDDrosfQGPNY%2Fscreen-shot-2018-09-11-at-01.05.52.png?generation=1536751301862991\&alt=media)

![imin:fullAddress could also be short](https://809863394-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LJbuIOE-TIcNUDnkUS7%2F-LMCVxLR6sL_9pBXgolT%2F-LMCVyAfSo61H3CM-2Va%2Fscreen-shot-2018-09-11-at-00.50.36%20\(1\).png?generation=1536751301850717\&alt=media)

Where a dedicated page is available for details on a single EventSeries or FacilityUse, ensure that the view does not look sparse when minimal data is available, and even consider designing two versions of the details page to cover both scenarios.

### Adaptive Calculations

For calculations, ensure that missing or unexpected data is factored in.

For example for an `aggregateRating` (not currently part of the OpenActive modelling specification, and only defined in schema.org, see [Namespaces](https://docs.imin.co/using-the-platform/context-and-field-names#schema-org) for more info) if your application has a 5-star rating system and an Event is presented where the `reviewRating` is 10 but the `bestRating` is not defined (and [defaulted to 5](https://schema.org/bestRating) according to schema.org), your calculation should treat the `aggregateRating` as missing completely if the calculation has insufficient data, instead of displaying 10 stars or a division-by-zero error.

```javascript
"aggregateRating": {
  "type": "AggregateRating",
  "reviewCount": 45,
  "ratingValue": 10,
  "worstRating": 0
}
```

### Missing Images

For image URLs, we do not currently monitor the image URLs provided in open data in real-time, and while we are always cleansing the raw data and working with providers to improve the quality of their data, there will be rare cases where an image URL is invalid.

We recommend that you use CSS to display a single default image or subtle background gradient if the image supplied doesn't render, to ensure that the user experience is not adversely affected in the rare case that an image is unavailable.

## Robust Data Processing

In JavaScript accessing deeply nested properties and making assumptions about the existence of properties can lead to unexpected errors for data structures with a high degree of variance.

### JSON Query Libraries

A number of JSON query libraries also exist that facilitate safe querying of data.

#### Recommended Library

We conducted a [benchmark](https://measurethat.net/Benchmarks/ShowResult/37962) of the most commonly used libraries for JSON Querying,  and the results clearly show [JSPath](https://github.com/dfilatov/jspath) to be the most performant by far. It significantly out-performed a plain JavaScript functional approach ("control"), and as such is our recommended default approach.

![Speed comparison of JavaScript query libraries ](https://809863394-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LJbuIOE-TIcNUDnkUS7%2F-LQ5yQaKvNZXue8FmsjS%2F-LQ6QCxf5mWIRFASfE5Y%2FScreen%20Shot%202018-10-31%20at%2000.00.54.png?alt=media\&token=6d004a25-8fe8-42b2-b370-dbdda1b3ba5b)

#### Example Queries

When expecting an array result, JSPath will alway return an array, with `[]` returned in the case of no matches.

{% code title="jspath-demo.js" %}

```javascript
return JSPath.apply('.subEvent.offers."beta:availableChannel"', result).join('<br />');
```

{% endcode %}

When expecting a single object or primitive, specifying `[-1]` at the end of the query will result in the query returning either the expected value, or `undefined` in the case of no matches.

```javascript
// Checks for existance of valid price and priceCurrency
const offer = JSPath.apply('."imin:aggregateOffer".publicAdult{.price > 0 && .priceCurrency > "."}[-1]', result);

// Always use browser built-in toLocaleString to render price
return offer ? offer.price.toLocaleString('en', {
  style: 'currency',
  currency: offer.priceCurrency
}) : '';
```

Additionally, if particular properties within a type are necessary for your processing, ensure they exist by including an existence check in the query such as `.startTime` and `.endTime` in the example below:

```javascript
var scheduleDates = JSPath.apply('.subEvent.eventSchedule{.type === "PartialSchedule" && .byDay === $day && .startTime && .endTime}', result, { day : 'schema:' + matchingDay })
```

#### Recommended Tooling

The [Online JSON Query Test ](http://www.jsonquerytool.com/#/JSPath)supports JSPath directly, and is our recommended tool for developing and testing JSON Queries.

{% embed url="<http://www.jsonquerytool.com/#/JSPath>" %}

#### Other Available Libraries

* [jsonata.org](https://developer.ibm.com/code/open/projects/jsonata/)
* [jmespath.org](http://jmespath.org/libraries.html)
* [json-query](https://www.npmjs.com/package/json-query)

### Alternative Approaches

There are a number of techniques to accessing property of a JSON object in a robust way, discussed in the following article. These are useful for simple implementations, where performance is less of a concern.

{% embed url="<https://medium.com/javascript-inside/safely-accessing-deeply-nested-values-in-javascript-99bf72a0855a>" %}

An extension of the most basic of these implementations allows for accessing of arrays and properties:

```javascript
const get = p => o =>
  p.reduce((xs, x) => {
  	if (Array.isArray(xs)) {
    	return xs.map(get([x])).filter(y => y !== null).flat();
    } else {
 	   	return (xs && xs[x]) ? xs[x] : null;
    }
  }, o);

// Example use
return get(['subEvent', 'offers', 'acceptedPaymentMethod'])(result).join('<br />');
```
