Skip to content

Submitting

Dillon Redding edited this page Dec 31, 2023 · 5 revisions

The submit function allows clients to execute an Action found within an entity received from the server. Because the response to submitting an action may not have Siren content, the result is a Response object.

import { submit } from '@siren-js/client';

// fields and action objects are from the Modeling page 👉
productCodeField.value = 'ABC123';
quantityField.value = 10;

let response = await submit(addItemAction);

If you can safely assume the server only responds with Siren content, you can easily combine submit with parse (see Parsing).

const entity = await submit(addItemAction).then(parse);

Instead of manually populating fields as shown above, you can use the ActionFiller visitor.

const addItemData = new ActionFiller({
  productCode: 'ABC123',
  quality: 10
});

await addItemAction.accept(addItemData);
// field's are now populated

response = await submit(addItemAction);

Providing a Base URL

You can provide a baseUrl in case the action's href is relative.

response = await submit(addItemAction, {
  baseUrl: 'http://api.example.io'
});

Configuring Fetch

Use the requestInit option to specify the options for the call to fetch.

response = await submit(addItemAction, {
  requestInit: {
    headers: {
      'Api-Key': 'abcdefghijklmnopqrstuvwxyz'
    }
  }
});

Validating Fields

When submit is called, each of the action's fields are validated. By default, all fields pass validation, effectively making validation an opt-in feature. For stricter validation, you can provide a Validator using the validator option.

A Validator is a function that accepts an array of Field objects and returns a ValidationResult. When validation passes, return an instance of a PositiveValidationResult. When validation fails, return an instance of a NegativeValidationResult.

import { NegativeValidationResult, PositiveValidationResult, Validator } from '@siren-js/client';

function isValid(field: Field): boolean {
  if (field.name === 'quantity' && field.value < 1) return false;
  if (!field.productCode) return false;
  return true;
}

const validator: Validator = (fields) => {
  const valid = fields.every(isValid);
  if (valid) return new PositiveValidationResult();
  else return new NegativeValidationResult();
};

await submit(addItemAction, { validator });

A NegativeValidationResult signals the submit function throw a ValidationError, which wraps the NegativeValidationResult.

try {
  await submit(addItemAction, { validator });
} catch (error) {
  if (error instanceof ValidationError) {
    console.log(error.result);
  }
}

You'll likely need more info than the NegativeValidationResult implementation gives you. Simply extend NegativeValidationResult with the additional fields you need. As long as you return a subclass of NegativeValidationResult, submit will interpret the result as failed validation.

class GoofedValidation extends NegativeValidationResult {
  constructor(readonly invalidFields: Field[]) {
    super();
  }
}

const informativeValidator: Validator = (fields) => {
  const invalidFields = fields.filter((field) => !isValid(field));
  if (invalidFields.length > 0) return new GoofedValidation(invalidFields);
  else return new PositiveValidationResult();
};

try {
  await submit(addItemAction, { validator: informativeValidator });
} catch (error) {
  if (error instanceof ValidationError && error.result instanceof GoofedValidation) {
    console.log('Fields failing validation:');
    error.result.invalidFields.forEach((field) => console.log(field.name));
  }
}

Serializing Fields

After the action's fields are validated, they are serialized. By default, fields are serialized using the defaultSerializer. To customize field serialization, provide a Serializer using the serializer option.

// TODO
Clone this wiki locally