Processors can essentially be seen as a function (in functional programming) or semantic block in programming. They are used by resources. They are completely abstracted from the actual resources themselves. All processors care about is inputs, processing and output.
They do not need to know where the inputs are coming from. They will validate the inputs against their requirements, and if the input is invalid, then an ApiException will be thrown.
All processors, whether they are endpoint, output, processing or security processors must extend ApiOpenStudio\Core\ProcessorEntity
or a child of this base class.
There is only one mandatory attribute.
The attribute is an array with the following indexes:
name
- Human-readable name.
description
- Human-readable description of the processor and what it does, plus anymenu
- The category heading in the processor list, under which it will appear.conditional
(optional (default: false
))
'contitional'
is set to true
: TreeParser will calculate all inputs not defined as 'contitional'
, and return the metadata of the result branch, instead of the final calculated result.return $this->meta->{input_name}
input
- an array of inputsEach input in the input array require s the following key/value pairs:
description
- Human-readable description of the input.cardinality
- The cardinality of values to this input, e.g.:
[0, 1]
- 0 or 1[0,'*']
- 0 to many.[1, '*']
- 1 to many.[1, 1]
- 1.[2, 5]
- 2 to 5.literalAllowed
- Allow a literal input. If set to false, the input must always come from alimitProcessors
- Limit the inputs to specific processors.limitTypes
- Limit the input type, e.g. to text, integer or float.limitValues
- Limit the final value of an input to a set literal values.conditional
(optional)
conditional
is set to true in the main processor definition (see above).default
- provide a default value if no input is received.Example:
/**
* {@inheritDoc}
*/
protected array $details = [
'name' => 'Example processor',
'machineName' => 'example_processor',
'description' => 'This is for an example detail.',
'menu' => 'Custom',
'input' => [
'input_1' => [
'description' => 'Input value 1.',
'cardinality' => [1, 1],
'literalAllowed' => true,
'limitProcessors' => ['var_request'],
'limitTypes' => ['text'],
'limitValues' => ['get', 'put'],
'default' => 'get',
],
],
];
The only required method is public process()
.
This takes no input in its contract.
You should always log the start of the processor processing and the processor name. Because there are potentially a lot of processors in a resource metadata tree, this helps developrs debug and find where an issue is occurring:
$this->logger->info('api', Processor: ' . $this->details()['machineName']);
It will fetch its values defined in the $details attribute, using the val()
function.
The val()
function can return the real value, or a DataContainer
. In general, you will want to use the real value within the process()
function. e.g.
$input1 = $this->val('input_1', true);
The output must always be a ApiOpenStudio\Core\DataContainer
. This ensures a consistent value is always pased between processors.
In order to fail gracefully, processors should trap all errors and throw an ApiOpenStudio\Core\ApiException
. This will be caught by the core processing code, and the message will be returned in the result with the defined error code, HTML response code and processor ID. e.g.
if (false) {
throw new ApiException("I could not process this input.", 6, $this->id, 400);
}
The core code will often find issues and will throw ApiException's. However, the core code will have no idea what processor was actually being processed at the time of the exception and will be unable to add the prcessor ID to the exception. It is good style to catch and rethrow these exceptions, with the processor ID. This makes it much easier for users and future developers to locate issues and bugs when using your processor.
i.e.
try {
$result = new DataContainer($myResult, 'array');
} catch (ApiException $e) {
throw new ApiException($e->getMessage(), $e->getCode, $this->id, $e->getHtmlCode());
}
ApiOpenStudio uses Monolog for it's logging, and there are several levels of debugging:
DEBUG
: Detailed debugging information.INFO
: Handles normal events. Example: SQL logsNOTICE
: Handles normal events, but with more important eventsWARNING
: Warning status, where you should take an action before it will become an error.ERROR
: Error status, where something is wrong and needs your immediate actionCRITICAL
: Critical status. Example: System component is not availableALERT
: Immediate action should be exercised. This should trigger some alerts and wake you up at night.EMERGENCY
: It is used when the system is unusable.The logger is available as an attribute in all processors:
$this->logger->info('api', My message');
ApiOpenStudio uses a DB mapper design pattern.
Use a table related mapper (e.g. ApiOpenStudio\Db\AccountMapper
to make requests against the Account table). Call one of the predefined methods in the mapper to make the SQL call. This will return an object, representing the row or rows that are the result of your query.
e.g.
$accountMapper = new AccountMapper($this->db);
$account = $accountMapper->findByName($name);
$account_id = $account->getAccid();