Skip to content

Loading and Validating Decisions

This guide explains how to load and validate decision records from a simple script.

Setup

Dependencies

The following packages are required:

Terminal window
npm install @noodlestan/designer-functions @noodlestan/designer-schemas

Configuring Designer Decisions

Add a Designer Decisions configuration file in the root of your project.

  • Directorydata/
    • …
  • Directorysrc/
    • …
  • dd.config.mjs
  • package.json

The minimum required configuration specifies only one or more DecisionSource from where to load decision data (DecisionInput).

In this example we configured the Sample and Demo Data decision sources and a local data/ directory for you to add more decisions, as well as the built-in schemas.

dd.config.mjs
import { defineConfig } from '@noodlestan/designer-functions';
import { DEMO_DATA, SAMPLE_DATA } from '@noodlestan/designer-decisions';
export default defineConfig({
store: {
decisions: [SAMPLE_DATA, DEMO_DATA, './data'],
},
});

In the API / Configuration section you can find all configuration options and helpers.

Loading input data

Add a script to load decisions and print eventual validation errors.

  • Directorydata/
    • …
  • Directorysrc/
    • …
  • Directoryscripts/
    • validate-decisions.js
  • dd.config.mjs
  • package.json

We will use the loadConfig() helper to load and validate the dd.config.mjs configuration first, and then the all-in-one staticStoreBuilder() to load and validate the decision data.

scripts/validate-decisions.js
import path from 'path';
import { createStoreContext, staticStoreBuilder, loadConfig } from '@noodlestan/designer-functions';
const config = await loadConfig();
const context = createStoreContext(config.store);
const build = staticStoreBuilder(context);

The build() functions returns a pre-validated decision store asynchronously. The store exposes all input records along with any errors that may have occurred while loading schemas and data sources, or when validating input data.

Add the following code to execute the builder and list each records’ name and model.

We will also format any eventual errors to better assist us if something goes wrong.

scripts/validate-decisions.js
import {
createStoreContext,
formatError,
loadConfig,
staticStoreBuilder,
} from '@noodlestan/designer-functions';
5 collapsed lines
const config = await loadConfig();
const context = createStoreContext(config.store);
const build = staticStoreBuilder(context);
const loadDecisions = async () => {
const store = await build();
const records = store.records();
records.forEach(({ name, model }) => console.info(name, model));
console.info(`🐘 ${records.length} records`);
context.errors().forEach(error => console.error(formatError(error)));
};
loadDecisions();

You can run the script directly from the command line:

Terminal window
node scripts/validate-decisions.js

If no errors occur, the output should look something like:

...
Sizing Space Scale space-scale/anchored
Card Thumb Minimum Size space-value/explicit
Avatar Minimum Size space-value/explicit
Avatar Sizes space-scale/anchored
🐘 67 records

Adding more decision data

As illustrated in the Decisions as Data guide, when using JSON files as data source, you are free to create sub-directories and name the files as you wish. Grouping decisions by type or scope is a good starting point.

Let’s start by adding a single color.json file in the data/ directory, where we can define color related decisions.

  • Directorydata/
    • color.json

Here, we created a couple of Color Value decisions based on the samples.

data/color.json
[
{
"model": "color-value/explicit",
"name": "Warm Color",
"params": {
"value": "orange"
}
},
{
"model": "color-value/explicit",
"name": "Cold Color",
"params": {
"value": "aqua"
}
}
]

If you run node scripts/validate-decisions.js again, you will see two new decisions at bottom of the list.

...
Avatar Minimum Size space-value/explicit
Avatar Sizes space-scale/anchored
Warm Color color-value/explicit
Cold Color color-value/explicit
🐘 68 records

If your records aren’t showing, lookout for error messages such as:

🐘 66 records
πŸŸ₯ Invalid DecisionSource "./data". Decision file does not contain an array.Path: "./data/color.json"

If you see this, make sure your file contains an array of decisions [{ ... }, { ... }] as opposed to a single {...} decision record.

The following error can also appear if your data has a syntax error. A single misplaced } or missing : will cause the JSON data to be unreadable. We recommend using an editor such as VS Code for visual feedback and assistance.

🐘 66 records
πŸŸ₯ Invalid DecisionSource "./data". Could not parse decision file. Expected ',' or ']' after array element in JSON at position 233. Path: "./data/color.json"

Validating input data

Decision data is automatically validated against JSON schemas as soon as it is loaded.

For instance, if the params are incomplete or don’t match the expected data types for the decision model, the decision will be marked with validation errors. In these cases, the decision will likely produce unexpected values so it’s good to know that there are issues in our data.

To inspect the validation errors we can format messages using the formatDecisionInputError() utility function.

scripts/validate-decisions.js
import {
createStoreContext,
formatError,
formatDecisionInputError,
loadConfig,
staticStoreBuilder,
} from '@noodlestan/designer-functions';
5 collapsed lines
const config = await loadConfig();
const context = createStoreContext(config.store);
const build = staticStoreBuilder(context);
const loadDecisions = async () => {
const store = await build();
const records = store.records();
records.forEach(({ name, model }) => console.info(name, model));
console.info(`🐘 ${records.length} records`);
store.inputErrors().forEach(error => console.error(formatDecisionInputError(error)));
context.errors().forEach(error => console.error(formatError(error)));
};
loadDecisions();

If we now introduce a couple of issues in or data, for instance deleting the value parameter from one color and set it to an invalid value in the second one…

data/color.json
[
{
"model": "color-value/explicit",
"name": "Warm Color",
"params": {
"value": "orange"
}
},
{
"model": "color-value/explicit",
"name": "Cold Color",
"params": {
"value": false
}
}
]

… and run our script again, the validation errors will be displayed at the bottom.

🐘 68 records
πŸŸ₯ Decision "Warm Color" /params (required) must have required property 'value'
πŸŸ₯ Decision "Cold Color" /params/value (type) must be object, string or number

Producing decision values

So far, the script only validates the input data against the schemas, guaranteeing that all the required attributes are present and that they are of the correct type.

Producing decision values may still result in errors if, for instance, a reference to another decision can not be resolved, or it resolves to a decision of the wrong type.

The following two utility functions help with this:

Remove the store.inputErrors().forEach(...) line, as it will become redundant, and add these 2 lines to the script:

scripts/validate-decisions.js
import {
createStoreContext,
formatDecisionStatus,
formatError,
loadConfig,
staticStoreBuilder,
produceDecisions
} from '@noodlestan/designer-functions';
7 collapsed lines
const config = await loadConfig();
const context = createStoreContext(config.store);
const build = staticStoreBuilder(context);
const loadDecisions = async () => {
const store = await build();
const records = store.records();
const produced = produceDecisions(store);
produced.decisions().forEach(status => console.info(formatDecisionStatus(status)));
console.info(`🐘 ${records.length} records`);
store.inputErrors().forEach(error => console.error(formatDecisionInputError(error)));
context.errors().forEach(error => console.error(formatError(error)));
3 collapsed lines
};
loadDecisions();

Without any errors, the output will now display the decision status, the name and model, and the value produced by the decision.

...
🟩 | Textarea Maximum Height | space-value/explicit | 300 |
🟩 | Card Thumb Minimum Size | space-value/explicit | 175 |
🟩 | Avatar Minimum Size | space-value/explicit | 50 |
🟩 | Avatar Sizes | space-scale/anchored | [50, 100, 150, 200] |

If we introduce a validation issue in one of the items, as in the previous example, the output will also include details about the error.

...
🟩 | Card Thumb Minimum Size | space-value/explicit | 175 |
🟩 | Avatar Minimum Size | space-value/explicit | 50 |
🟩 | Avatar Sizes | space-scale/anchored | [50, 100, 150, 200] |
🟨 | Warm Color | color-value/explicit | #000000 | 1 errors, 1 warnings
> 🟨 Decision "Warm Color" /params (required) must have required property 'value'
> 🟨 Invalid input data for a ColorValue in {"$name":"Warm Color"}: undefined

Summarizing and catching errors

The produced object reports counts for decision records, and different types of errors in a single string.

scripts/validate-decisions.js
19 collapsed lines
import {
createStoreContext,
formatDecisionStatus,
formatError,
loadConfig,
staticStoreBuilder,
produceDecisions
} from '@noodlestan/designer-functions';
const config = await loadConfig();
const context = createStoreContext(config.store);
const build = staticStoreBuilder(context);
const loadDecisions = async () => {
const store = await build();
const records = store.records();
const produced = produceDecisions(store);
produced.decisions().forEach(status => console.info(formatDecisionStatus(status)));
console.info(`🐘 ${records.length} records`);
console.info('🐘', produced.summary());
4 collapsed lines
context.errors().forEach(error => console.error(formatError(error)));
};
loadDecisions();

You can use it to summarize all counts in one line.

🟨 Decision "Sizing Space Scale" /params/after (required) must have required property 'steps'
🐘 59 records, 0 errors, 1 warnings

If you are running this as part of your build process you will probably want to exit the script with an error code.

scripts/validate-decisions.js
20 collapsed lines
import {
createStoreContext,
formatDecisionStatus,
formatError,
loadConfig,
staticStoreBuilder,
produceDecisions
} from '@noodlestan/designer-functions';
const config = await loadConfig();
const context = createStoreContext(config.store);
const build = staticStoreBuilder(context);
const loadDecisions = async () => {
const store = await build();
const produced = produceDecisions(store);
produced.decisions().forEach(status => console.info(formatDecisionStatus(status)));
console.info('🐘', produced.summary());
context.errors().forEach(error => console.error(formatError(error)));
if (produced.hasErrors()) {
throw new Error(`Errors encountered producing decisions.`);
}
};
loadDecisions().catch(err => {
console.error(err);
process.exit(1);
});

Next steps

The next guides provide step by step instructions on practical uses cases for your decision data:

See also