Reactor
As a reactor, you can provide actions that the Deviation Processor can trigger in response to deviations. This guide explains how to provide an action as a reactor, how to process a deviation, and how to update a deviation.
Provide an Action as a Reactor
Reactors can offer one or multiple actions that the Deviation Processor can trigger in response to specific deviations. To execute the provided action as part of a reaction within the Deviation Processor, the processor calls an endpoint on the reactor module that must comply with the following standardized interface. The URL of this action’s endpoint is supplied to the Deviation Processor during the registration of the action at the Deviation Processor.
Ensure that the Deviation Processor has the necessary permissions to call the endpoint of the specific action on your module. For IAS modules using Helm deployment, permissions can be granted to the Deviation Processor from within your module’s Helm chart. Please consult the IAS Operations Manual for guidance on providing permissions to other modules. |
The URL of the action’s endpoint is decided upon by you, although the HTTP method has to be POST and the payload is a JSON object containing the following:
-
reactionId
: The ID of the reaction entity within the Deviation Processor that called the registered action’s endpoint. -
deviation
: An object containing all relevant information about the deviation that triggered the reaction. -
userDefinedFields
: An object with an arbitrary structure defined by the reactor module during the action registration process.
For more details on the registration of actions, please refer to here.
{ "userDefinedFields": { // keys and property types depending on the registered action "solvergroupid": "some id", "tickettitle": "this is a title" }, "reactionId": "some id", "deviation": { "code": "error123", // string, max length 256 (as specified by Master Data Management) "module": "Nexeed CM", // string, max length 50 "detector": "Rule_4356", // string, max length 50 "affectedId": "device 123", // string, max length 256 "affectedSpecificTypeId": "device type 123", // string, max length 256 "affectedGenericType": "Device", // string, enum with values: Device, Shift, Process, Product, Facility "affectedDomain": "Condition Monitoring", // string, max length "affectedName": "name of device 123", // string, max length 200 "detectionCount": 0, // int "facilityId": "string", // string, max length 256 "severity": "LOW", // string, enum with values: LOW, MEDIUM, HIGH "description": "description of the deviation", // string, max length 200 "detectorUrl": "http://deviation.detector", // string, URI, max length 2083 "actualValue": "47 C°", // string, max length 50 "expectedValue": "between 30° and 35°", // string, max length 50 "title": "max temp exceeded", // string, max length 50 "kpi": "Quality", // string, max length 200 "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6", // string, uuid "state": "Open", // string, enum with values: Open, Processed, Closed "firstDetectedAt": "2022-12-05T07:49:43.335Z", // string, datetime with offset in iso format "firstProcessedAt": "2022-12-05T07:49:43.335Z",// string, nullable, datetime with offset in iso format "lastProcessedAt": "2022-12-05T07:49:43.335Z", // string, nullable, datetime with offset in iso format "closedAt": "2022-12-05T07:49:43.335Z", // string, nullable, datetime with offset in iso format "firstReceivedAt": "2022-12-05T07:49:43.335Z", // string, ISO datetime with offset "lastDetectedAt": "2022-12-05T07:49:43.335Z", // string, ISO datetime with offset "firstReactedAt": "2022-12-05T07:49:43.335Z", // string, nullable, datetime with offset in iso format "lastReactedAt": "2022-12-05T07:49:43.335Z" // string, nullable, datetime with offset in iso format } }
User-defined Fields
User-defined fields are sent to reactor modules as part of the payload to the respective action endpoint. These fields can include information entered directly by the user in manual or quick reactions, as well as information automatically fetched from the deviation using template variables for quick reactions.
Carefully consider which information to include in the user-defined fields, as the deviation is also part of the reaction payload. Avoid duplicating all information from the deviation, as this would create unnecessary integration effort. Since all these fields must be configured by the user, it can be frustrating for them to map all deviation information to your specific field names. Always prefer mapping information from the deviation within your endpoint. For further insights on this importance, please refer to the concept on standardization. |
Validation
Before the user-defined fields are sent to the reactor, the JSON object is validated against a validation schema provided by the respective reactor module during registration. Please consult the documentation of your web application framework for guidance on generating valid JSON schema specifications from your API model the user-defined fields. If these fields change over time, the registered validation schema must be updated in the Deviation Processor by re-registering the action.
User-defined Fields Input in the UI
When a user triggers a reaction manually or sets up a quick reaction, the data to be delivered as part of the user-defined fields must be provided. Therefore, a form must be rendered in the user interface of the Deviation Processor, as shown below:

Previously, the Deviation Processor offered two approaches to render the user-defined fields form: embedded views and the UI-schema approach. However, the UI-schema approach is deprecated as of version 1.8.0 (2024.02) and is subject to removal soon. |
Using an embedded view for user-defined fields allows for greater customizability on the reactor’s side and a more dynamic approach to restrictions, making it the preferred method for configuring user-defined fields in the Deviation Processor.
Backend Implementation Guide
Register an embeddable view in portal for the context NEXEED_SM_DEVIATION_PROCESSOR_REGISTERED_ACTION
with a unique id
and a localized name. Below is a sample
portal configuration for an embedded view for configuring user-defined fields:
"Contexts": { "items": [ { "contributionId": "4d08a3d4-3139-45b1-8174-66e49e0b9cf5", "contextName": "NEXEED_SM_DEVIATION_PROCESSOR_REGISTERED_ACTION", "contextType": "EMBEDDED_VIEW", "contextVersion": "1.0", "resourceId": "urn.com.bosch.nexeed.safety.action", "resourceType": "api", "contribution": { "url": "{info.baseurl}/ui/safety/event?tenantId=##tenantId##", "name": "Create Safety Event", "localization": { "name": { "de": "Sicherheitsereignis erstellen" } } } } ] }
The backend hosting the embedded view must ensure that its Content Security Policy (CSP) permits embedding within the portal and all its subdomains. This can be
achieved by correctly configuring the frame-ancestors directive in the CSP. Additionally, please take into account subdomain deployments.
|
Frontend Implementation Guide
The embedded view must…
Emit the following two events:
NEXEED_SM_DEVIATION_PROCESSOR_REGISTERED_ACTION_INITIALIZATION
-
Informs the host view that the embedded view is initializing.
-
Provides the host view the capability to return the initial form data in the callback.
-
Conditions are sent in the case the embedded view is called in the context of a Quick Reaction
-
Deviation is sent in the case the embedded view is called in the context of a Manual Reaction
-
This infomation is commonly used for custom logic in the embedded view, such as initializing certain fields with common defaults - depending on the context
-
Iframe Integration Library Event:
informHostViewWithCallback
Callback data model:
interface Data { userDefinedFields: UserDefinedFields | null | undefined; // Initial form content conditions: Conditions | null | undefined; // Quick Reaction conditions, set when editing/creating a Quick Reaction. Undefined/null in other cases, e.g., when creating a Manual Reaction. deviation: DeviationDetails | null | undefined; // Information about deviation, set when creating a Manual Reaction. Undefined/null in other cases, e.g., when editing/creating a Quick Reaction. disabled: boolean; // If the form should be disabled. If true the form will be displayed in read-only mode. }
Type Declarations
interface UserDefinedFields: { [key: string]: string[] | string | number | boolean | null; } interface Conditions { detectionCount: number | null; affectedGenericType: AffectedGenericType[] | null; severity: DeviationSeverity[] | null; firstDetectedAt: TimeRangeOptions | null; lastDetectedAt: TimeRangeOptions | null; module: string[] | null; code: string[] | null; detector: string[] | null; facilityId: string[] | null; affectedFacilities: string[] | null; // matches all deviations that have facility as affectedGenericType and a facility from the list as facilityId affectedProcesses: string[] | null; // same as above for processes affectedDevices: string[] | null; // same as above for devices affectedShifts: string[] | null; // same as above for shifts affectedProducts: string[] | null; // same as above for products affectedSpecificDeviceTypes: string[] | null; // matches all deviations with affectedSpecificTypeId from the list. Currently only supports DeviceTypeIds. affectedDomains: string[] | null; } interface DeviationDetails { id: string; firstDetectedAt: Date; firstReceivedAt: Date; lastDetectedAt: Date; firstProcessedAt: Date | null; code: string; module: string; detector: string; affectedId: string; affectedGenericType: AffectedGenericType; affectedName: string | null; affectedSpecificTypeId: string | null; affectedDomain: string | null; detectionCount: number; facilityId: string | null; severity: DeviationSeverity; description: string | null; detectorUrl: string | null; actualValue: string | null; expectedValue: string | null; kpi: string | null; facilityName: string | null; title: string | null; lastProcessedAt: Date | null; closedAt: Date | null; firstReactedAt: Date | null; lastReactedAt: Date | null; state: DeviationState; impacts: DeviationImpact[] | null; hints: DeviationHint[] | null; reactions: DeviationReaction[] | null; deviationProcessings: DeviationProcessing[] | null; deviationRisks: DeviationRisk[] | null; userDefinedTitle: string | null; userDefinedDescription: string | null; } enum DeviationState { Open = 'Open', Processed = 'Processed', Closed = 'Closed', } enum AffectedGenericType { Device = 'Device', Shift = 'Shift', Process = 'Process', Product = 'Product', Facility = 'Facility' } enum DeviationSeverity { LOW = 'LOW', MEDIUM = 'MEDIUM', HIGH = 'HIGH', } enum TimeRangeOptions { Last30Minutes = 'Last30Minutes', LastHour = 'LastHour', Last2Hours = 'Last2Hours', Last3Hours = 'Last3Hours', Last4Hours = 'Last4Hours', Last8Hours = 'Last8Hours', Last24Hours = 'Last24Hours' } interface DeviationImpact { title: string; value: number; description: string | null; unit: string; } interface DeviationHint { description: string; url: string | null; attachmentUrl: string | null; manuallyAdded: boolean; } interface DeviationReaction { id: Guid; timestamp: Date; manual: boolean; registeredActionId: string; registeredActionName: string; registeredActionModule: string; dueDate: Date | null; displayName: string; error: string | null; userDefinedFields: UserDefinedFields; resultingArtifactId: string | null; resultingArtifactType: string | null; state: DeviationReactionState; } enum DeviationReactionState { New = 'New', Retrying = 'Retrying', SentToReactor = 'SentToReactor', ErrorSendingToReactor = 'ErrorSendingToReactor', AutomaticallyResolved = 'AutomaticallyResolved', ManuallyResolved = 'ManuallyResolved', } interface DeviationProcessing { processedAt: Date; description: string; reactionId: Guid | null; module: string | null; url: string | null; closeDeviation: boolean; resolveReaction: boolean; resolveUnresolvedReactions: boolean; } interface DeviationRisk { id: Guid; kpiId: string; title: string; description: string | null; openInHours: number | null; influenceOnKpi: number | null; expectedResult: number | null; remainingResult: number | null; state: string; createdAt: Date; closedAt: Date | null; }
Example
Consider the special cases where the JSON value of a user defined field needs to be mapped to a different type. In the example the userDefinedFields.timeOfEvent is an ISO 8601 string which needs to be converted to the date object. |
import { embeddedView } from '@bci-portal/iframe-integration'; ... ngOnInit(): void { embeddedView.informHostViewWithCallback('NEXEED_SM_DEVIATION_PROCESSOR_REGISTERED_ACTION_INITIALIZATION', null) .then((event: { sender: MessageEventSource | null; data: BaseMessage<string, Record<string, any>, number> }) => { if (event.data.value != null) { const userDefinedFields = event.data.value.userDefinedFields; const formValue: SafetyEventFormModel | null = userDefinedFields != null ? { type: userDefinedFields.type, description: userDefinedFields.description, // Important! Conversion necessary! timeOfEvent: new Date(userDefinedFields.timeOfEvent) } : null; this.form.setValue(formValue, { emitEvent: false }); if (event.data.value.disabled) { this.form.disable(); } } } ); }
NEXEED_SM_DEVIATION_PROCESSOR_REGISTERED_ACTION_USER_DEFINED_FIELD
-
Sends the new form value and status to the host view whenever they change.
-
If the embedded view receives an initial form value from the host view in the callback of the
NEXEED_SM_DEVIATION_PROCESSOR_REGISTERED_ACTION_INITIALIZATION
event,NEXEED_SM_DEVIATION_PROCESSOR_REGISTERED_ACTION_USER_DEFINED_FIELD
needs to be called after the form initialization as well, so that the host view knows the status/value of the form after initialization. An example where the propagation of the form state to the host view after initialization is needed, is if a previously optional field becomes mandatory. Then the value of the old user defined fields becomes invalid. -
Iframe Integration Library Event:
informHostView
Data model:
interface Data { userDefinedFields: UserDefinedFields | null; // Form content valid: boolean; touched: boolean; dirty: boolean; }
Example
Some form values might need to be converted to a valid JSON value before being sent to the host view. |
import { embeddedView } from '@bci-portal/iframe-integration'; ... ngOnInit(): void { this.form.valueChanges.pipe(takeUntil(this.ngUnsubscribe)).subscribe(value => { const data: Record<string, unknown> = { valid: this.form.valid, touched: this.form.touched, dirty: this.form.dirty, userDefinedFields: value }; embeddedView.informHostView('NEXEED_SM_DEVIATION_PROCESSOR_REGISTERED_ACTION_USER_DEFINED_FIELDS', data); }); }
React to the following event:
NEXEED_SM_DEVIATION_PROCESSOR_REGISTERED_ACTION_INITIALIZATION
-
The embedded view needs to initialize the form with the user defined fields specified in the callback data of this event, if any.
-
The embedded view needs to disable the form if the flag from the callback data is true.
-
The embedded view might need to configure the form based on the conditions received in the callback data, if any.
-
See callback data model in the above description of the event.
Process a Deviation as a Reactor
Once the corrective action within the reactor module is completed (e.g., a ticket in Ticket Management is closed), the reactor must notify the Deviation Processor of
this completion. This is accomplished by sending a request to the POST /api/{apiVersion}/{tenantId}/public/{deviationId}/processing/{reactionId}
endpoint. For
detailed information about the payload, please refer to the API documentation.
The deviationId
and reactionId
required for this request can be derived from the payload of the reaction request sent to the reactor module by the Deviation
Processor. These IDs will always be included in that payload.
To indicate that the reaction has been resolved, set the resolveReaction
flag to true
in the processing request payload. This action will mark both the reaction
and the associated deviation as processed. It is important to set the flag only if the corrective action has been successfully completed. If there are no remaining
reactions other than those triggering notifications within the Notification Service for the associated deviation, the deviation will be closed automatically.
Update a Deviation as a Reactor
If there are changes to data related to a reaction entity in the Deviation Processor, the reactor module can update the deviation by sending a request to the PATCH
/api/{apiVersion}/{tenantId}/public/{deviationId}/reaction/{reactionId}
endpoint. For detailed information about the payload, please refer to the
API documentation.
The deviationId
and reactionId
required for this request can be obtained from the payload of the reaction request sent to the reactor module by the Deviation
Processor. These IDs will always be included in that payload.
Currently, the only supported update is for the dueDate
of the reaction. For instance, if a user in Ticket Management delays the due date of a ticket, this change
should also be reflected in the corresponding reaction entity within the Deviation Processor.
Register an Action as a Reactor
Reactors must register all available actions intended for use by the Deviation Processor, as it does not have prior knowledge of any reactors (standardization). This
registration is accomplished by calling the POST /api/{apiVersion}/{tenantId}/public/registeredaction
endpoint during the module’s startup.
In addition to registering new actions, this endpoint can also be used to update existing registered actions within the Deviation Processor’s storage. Actions should be updated in scenarios such as when an old reaction endpoint is deprecated or relocated, or if the schema for user-defined fields has changed.
The payload for the registration request includes information about the structure and validation of the user-defined fields, as well as the URL of the action endpoint. For a detailed description of the payload, please refer to the API documentation.
For modules developed in .NET Core, the Deviation Processor’s development team provides a ready-to-use NuGet package to facilitate the registration process. For more information about this package please refer to the client documentation. |