Business Integration Solutions documentation
Custom pipeline element
Business Integration Solutions provides a standard set of endpoints, events, and activities. The pipeline architecture includes extension points that let you extend this standard set, for example, a custom endpoint that connects to a specific web service or implements an unsupported format. You can extend the set without changing the standard product.
Prerequisites
The following prerequisites are required:
- Experience developing in AL.
- Understanding of Business Integration Solutions and especially the concept of pipeline and messages.
- A Dynamics 365 Business Central development license.
- Experience with the Dynamics 365 Business Central test framework.
How to: Create a custom element
Use this task when you want to extend the list of standard activities with custom types.
Steps
The pipeline uses a generic system that you can enhance with specific configuration for each activity.
| Name | Description | Mandatory |
|---|---|---|
| Setup table | Table with a fixed primary key that maintains the activity configuration. | No |
| Setup page | Page that maintains the activity configuration. | No |
| Handler codeunit | The codeunit that implements the functionality of the activity. | Yes |
| Configuration check codeunit | A codeunit that runs the activity-specific tests, activated via the Check Configuration function. | No |
| Configuration XmlPort | An XmlPort that exchanges the details of the pipeline element to a fixed format. | No |
Develop a new setup table that implements the specific activity settings.
Develop a new page based on the setup table.
Develop a handler codeunit.
Develop a configuration check codeunit.
Develop a configuration XmlPort.
Create an Element Type record for the activity. The element type defines the settings for an activity and connects the Dynamics 365 Business Central objects to the configuration.
Using the Register function of the Handler Codeunit can automate the registration process.
How to: Create a setup table for an element
Use this task when you want to create a setup table with specific settings for an endpoint, activity, or event.
Steps
The setup table registers specific settings for a pipeline element, letting you extend the framework by adding new settings with specific fields and validations.
Create a new AL table object.
Each activity has its own table that maintains the specific settings, enabling validations relevant only to that activity. Since this table is an extension of the generic BIS Pipeline Element table, it must have the same primary key. The primary key consists of the following fields:
Field no. Field name Data type Length 1 Source Type Integer 2 Source No. Code 20 3 Activity Code Code 20 Add the specific fields for the custom pipeline element.
Use a specific range for new fields, for example, 50000 and higher.
When a setup table is not needed for your custom pipeline element, the register function should use the standard STAEDEAN page BIS No Settings Setup.
How to: Create a handler codeunit for an element
Create a handler codeunit for a custom endpoint, activity, or event to implement the behavior of the pipeline element.
Steps
The handler codeunit implements the actual behavior of a pipeline element. The OnRun trigger must have a signature matching the element type. The following signatures are supported:
| Element type | Subtype | Table |
|---|---|---|
| Endpoint | Reader | Job Queue Entry |
| Endpoint | Writer | BIS Message Queue |
| Activity | BIS Message Queue | |
| Event | Job Queue Entry |
Create a new AL codeunit.
Add the appropriate code to implement the behavior of the pipeline element.
(Optional) Add a new function called
Register. This function enables automated registration of the pipeline element when your extension installs.An install codeunit can auto-register custom elements.
// Example of register method for an endpoint local procedure Register(sender: Codeunit "BIS API Pipeline Setup") var CustomElementDescription_Txt: Label 'Description of what your element does'; CustomElementCode: Code[20]; begin CustomElementCode := 'CustomElementCode'; sender.RegisterEndpoint( CustomElementCode, CustomElementDescription_Txt, 'Setup Table Id', 'Setup Page Id', 'Handler codeunit id', 'Configuration check handler codeunit id', 'Xml port handler id', 'ElementSubType - Option that changes depending on what you are trying to register'); end;To enable logging for your new custom element, subscribe to two events from codeunit
BIS API Pipeline Setup:[EventSubscriber(ObjectType::Codeunit, codeunit::"BIS API Pipeline Setup", 'OnEnableChangeLog', '', true, true)] local procedure OnChangelogEnable(var sender: codeunit "BIS API Pipeline Setup") begin Sender.AddTableToChangeLog("Your setup table id"); end; [EventSubscriber(ObjectType::Codeunit, codeunit::"BIS API Pipeline Setup", 'OnDisableChangeLog', '', true, true)] local procedure OnChangeLogDisable(var sender: codeunit "BIS API Pipeline Setup") begin Sender.RemoveFromChangelog("Your setup table id"); end;
How to: Create a configuration check codeunit for an element
Use this task when you want to create a configuration check codeunit to verify the settings for an endpoint, event, or activity.
Steps
A configuration check codeunit is a Dynamics 365 Business Central codeunit executed by a special test runner codeunit. This construction lets you implement tests in separate functions that execute individually. Errors that occur during this run capture in the test handler and write to a log table.
Create a new AL codeunit.
Declare global variables:
var CustomPipelineElement: Record "Your custom setup table"; ConfigTestRunner: Codeunit "BIS Configuration Test Runner";(Optional) Add initialization code to the
OnRuntrigger:trigger OnRun() var RecRef: RecordRef; begin ConfigTestRunner.CurrentRecord(RecRef); RecRef.SetTable(CustomPipelineElement); RunTests(); // In RunTests, call your test cases, see step 5 for a full example end;Add try functions to test your configuration.
When porting to cloud, replace test functions (not supported in SaaS) with try functions, see the full example below.
Ensure the configuration check codeunit is included in the
Registerfunction of the handler codeunit. See How To: Create a handler codeunit for an element.
Example of a configuration check codeunit from the Attachment Generator pipeline element:
codeunit 11070201 "BIS Attachment Generator Test"
{
trigger OnRun()
var
RecRef: RecordRef;
begin
ConfigTestRunner.CurrentRecord(RecRef);
RecRef.SetTable(AttachmentGenerator);
AttachmentLine.SetRange("Source Type", AttachmentGenerator."Source Type");
AttachmentLine.SetRange("Source No.", AttachmentGenerator."Source No.");
AttachmentLine.SetRange("Activity Code", AttachmentGenerator."Activity Code");
RunTests();
end;
var
AttachmentGenerator: Record "BIS Attachment Generator";
AttachmentLine: Record "BIS Attachment Generator Line";
ConfigTestRunner: Codeunit "BIS Configuration Test Runner";
local procedure RunTests()
var
CheckAttachmentsLbl: Label 'CheckAttachments', Locked = true;
CheckAttachmentGeneratorTypeLbl: Label 'CheckAttachmentGeneratorType', Locked = true;
begin
if not CheckAttachments() then
ConfigTestRunner.LogErrorMessage(CheckAttachmentsLbl, GetLastErrorText());
if not CheckAttachmentGeneratorType() then
ConfigTestRunner.LogErrorMessage(CheckAttachmentGeneratorTypeLbl, GetLastErrorText());
end;
[TryFunction]
procedure CheckAttachments()
begin
AttachmentLine.FindSet();
repeat
case AttachmentLine."Attachment Type" of
AttachmentLine."Attachment Type"::Report:
begin
AttachmentLine.TestField("Object No.");
AttachmentLine.TestField("Attachment Name");
end;
AttachmentLine."Attachment Type"::File:
AttachmentLine.TestField("Attachment Content");
end;
until AttachmentLine.Next() = 0;
end;
[TryFunction]
procedure CheckAttachmentGeneratorType()
var
GeneratorEnum: Enum "BIS Attachment Generator";
GeneratorNumber: Integer;
LineGeneratorNotValidErr: Label 'Attachment line %1 generator not valid. Please select from the available options.', Comment = '%1 Line No.';
begin
AttachmentLine.FindSet();
repeat
Evaluate(GeneratorNumber, Format(AttachmentLine.Generator, 0, 2));
if not GeneratorEnum.Ordinals.Contains(GeneratorNumber) then
Error(LineGeneratorNotValidErr, AttachmentLine."Line No.");
until AttachmentLine.Next() = 0;
end;
}
How to: Create a Configuration XmlPort for an element
Use this task when you want to create a configuration XmlPort for an endpoint, event, or activity.
Steps
The configuration XmlPort lets specific XML elements exchange for a pipeline element. Using an XmlPort for importing and exporting configuration settings decouples the internal table structure from an external representation of the data.
Create a new AL XmlPort.
Define the layout of the XmlPort.
The XmlPort layout does not need to include all data from the setup table. STAEDEAN does not export or import client-sensitive data such as passwords or credit card information.
Add a new function named
GetTargetRecordand call it in theOnAfterInitRecordtrigger of the root table in the port. This function is mandatory for importing data. The generic part of the configuration package reader calls it to set the primary key fields of the pipeline element.... trigger OnAfterInitRecord() begin GetTargetRecord(); "Your custom table" := TargetRecord; end; ... var TargetRecord: Record "Your custom table id"; procedure GetTargetRecord() var PackageEvents: codeunit "BIS Package Events"; NewTarget: RecordRef; begin PackageEvents.GetTargetRecord(NewTarget); if NewTarget.Number = "Your custom table id" then NewTarget.SetTable(TargetRecord); end;Ensure the configuration XmlPort is included in the
Registerfunction of the handler codeunit. See How To: Create a handler codeunit for an element.
Example of an XmlPort from the Azure File Reader pipeline element:
xmlport 11070207 "BIS Az File Endpoint"
{
FormatEvaluate = Xml;
schema
{
tableelement("BIS Az File Reader Setup"; "BIS Az File Endpoint Setup")
{
XmlName = 'AzFileReader';
textattribute(Version)
{
trigger OnBeforePassVariable()
begin
Version := '1.0';
end;
}
fieldelement(FolderName; "BIS Az File Reader Setup"."Folder Name") { }
fieldelement(LeaveFilesOnFileSystem; "BIS Az File Reader Setup"."Leave Files") { }
fieldelement(ReadNewFilesOnly; "BIS Az File Reader Setup"."Read New Files Only") { }
fieldelement(FileNameFilter; "BIS Az File Reader Setup"."File Name") { }
fieldelement(FileShare; "BIS Az File Reader Setup"."File Share") { }
fieldelement(StorageAccount; "BIS Az File Reader Setup"."Storage Account") { }
fieldelement(Overwrite; "BIS Az File Reader Setup".Overwrite) { }
fieldelement(UseOriginalMessage; "BIS Az File Reader Setup"."Use Original Message") { }
trigger OnAfterInitRecord()
begin
GetTargetRecord();
"BIS Az File Reader Setup" := TargetRecord;
end;
}
}
var
TargetRecord: Record "BIS Az File Endpoint Setup";
procedure GetTargetRecord()
var
PackageEvents: codeunit "BIS Package Events";
NewTarget: RecordRef;
begin
PackageEvents.GetTargetRecord(NewTarget);
if NewTarget.Number = Database::"BIS Az File Endpoint Setup" then
NewTarget.SetTable(TargetRecord);
end;
}