Build integrations
Adapters
The adapter contract, every built-in adapter and its properties, custom adapters
Adapters do the work at each stage of a subscription. Bitween has four kinds.
| Kind | Stage | Contract |
|---|---|---|
| Receiver | Pulls items for a scheduled job | IInfolinkReceiver: Initialize, ListFiles, GetFile(id), DeleteFile(id), Finalize |
| Validator | Checks API input before an exchange is created | IInfolinkValidator: Validate(file) returns success or a list of errors |
| Mapper | Transforms the input | IInfolinkHandler: Handle(file) returns the output |
| Handler | Delivers and returns an optional response | IInfolinkHandler: Handle(file) returns the response |
The contracts come from SimplyWorks.PrimitiveTypes. The payload is an XchangeFile with text Data, a Filename, a ContentType and a BadData flag. Notifiers and retry alerts reuse handler adapters.
Native and custom adapters#
- Native adapters are compiled into Bitween and run in process. Their ids start with
Native, such asNativeHttpHandler. - Custom adapters are separate .NET console programs that
SimplyWorks.Serverlessdownloads from object storage. A classic custom adapter runs as a new process for each call. A resident one runs as a long-lived process that keeps its connections open. - Data source adapters are resident adapters for brokers (
bitween.bus.rabbitmq,bitween.bus.sqs) and databases (bitween.db.postgresql,bitween.db.mysql,bitween.db.sqlserver,bitween.db.oracle). A subscription binds them to a data source. See Data sources, External brokers and Databases.
An id starting with native, ignoring case, is a native adapter. For any other id, Bitween reads the package's metadata and runs it as resident or classic. Resident adapters only run on nodes with Bitween:BusProvidersEnabled.
Properties#
Each adapter declares its properties. The UI shows which are required, which are secret, and each one's description and default.
- Tokens. Values can contain
{{partner.KEY}}and{{globals.SET.KEY}}. Bitween substitutes them ignoring case, global values first, when an exchange is created. Unresolved tokens stay as written. Receivers only get global values, and notifier properties get neither. - Secrets. Secret values are never sent to the browser. The API returns
__private__instead, and sending__private__back keeps the stored value. If Bitween cannot describe an adapter, it masks every property. - Required properties are checked when a subscription is saved. A blank value counts as missing.
xchangeidis added to mapper and handler properties at run time.- A value that does not convert to the property's type, such as
BatchSize=abc, silently falls back to the default. - The admin UI loads every adapter of a kind, with its properties, in one call. Descriptions of custom adapters are cached on each node, so a newly uploaded version can show its old properties for a while.
Rebex license#
The FTP/SFTP adapters and the Rebex POP3 receiver use the commercial Rebex library. They are hidden from the adapter pickers until a key is saved in the Rebex license key setting, which takes effect without a restart.
Handlers#
NativeHttpHandler#
Sends the payload to an HTTP endpoint.
| Property | Default | Notes |
|---|---|---|
Url (required) |
When the payload is present and the URL contains {{, it is rendered as a Liquid template over the payload, so https://api.example.com/orders/{{ order.id }} works. |
|
Verb |
post |
get, put or delete. Any other value, including patch, sends a POST. GET sends no body. |
AuthType |
ApiKey, Bearer, Basic, Login or OAuth2, matched case-sensitively. Empty means no authentication. |
|
ApiKey (secret) |
Used by ApiKey. |
|
LoginUsername |
Used by Basic and Login. |
|
LoginPassword (secret) |
Used by Basic and Login, and as the token for Bearer. |
|
LoginUrl |
Token endpoint for Login and OAuth2. |
|
ClientId, ClientSecret (secret) |
Used by OAuth2. |
|
ContentType |
application/json |
application/x-www-form-urlencoded form-encodes a flat JSON object. multipart/form-data sends the payload as one part named file. Other types send the payload as text. |
Headers (secret) |
Extra headers as Name:Value pairs separated by commas. A value cannot contain a colon. |
|
CorrelationId |
Sent as the request-context-correlation-id request header. |
|
DefaultRequest |
Body to send when the payload is empty. |
AuthType |
Behaviour |
|---|---|
ApiKey |
Adds the header ApiKey: {ApiKey} |
Bearer |
Adds Authorization: Bearer {LoginPassword} |
Basic |
Basic authentication with LoginUsername and LoginPassword |
Login |
POSTs Email and Password as JSON to LoginUrl, reads Jwt from the reply, and sends it as a bearer token |
OAuth2 |
Requests a client credentials token from LoginUrl, reads access_token, and sends it as a bearer token |
| Reply status | Result |
|---|---|
| 2xx or 3xx | Success. The body becomes the response file. |
| 4xx | Bad response. The body is kept and flagged, and retry policies see it as a BadResult. |
| 5xx, or below 200 | The exchange fails with the status and body, so retry policies see it as an Error. |
NativeSmtpHandler#
Sends an email built from the payload.
| Property | Default | Notes |
|---|---|---|
Host (required) |
||
Port |
587 |
Port 465 uses implicit TLS. Other ports use STARTTLS when UseTls is on. |
UseTls |
true |
TLS is required when on, never opportunistic. |
Username |
From |
|
Password (secret) |
Authenticates only when set, and only over a secure connection. | |
From (required), FromName |
||
To (required), Cc, Bcc |
Comma-separated addresses. | |
Subject (required), Body (required) |
Scriban templates over the payload, with the same syntax as the legacy JSON mapper. They are rendered only when the payload is JSON, otherwise sent as written. | |
IsHtml |
true |
Server certificates must be valid. A chain whose only problem is that revocation could not be checked is accepted. The response file holds the rendered subject.
NativeS3UploadHandler#
Writes the payload to an S3-compatible bucket.
| Property | Default | Notes |
|---|---|---|
AccessKeyId, SecretAccessKey (secret), ServiceUrl, BucketName |
All required. | |
FolderName |
Ignored when FileName is set. |
|
FileName |
Full object key. When empty, the key is {FolderName}/{yyyyMMddHHmmss}_{guid}.{FileExtension}. |
|
FileExtension |
||
ContentType |
text/plain |
The response file holds the object key.
NativeAzureBlobUploadHandler#
Writes the payload to an Azure Blob container.
| Property | Notes |
|---|---|
ConnectionString (required, secret), ContainerName (required) |
|
FileName |
Blob name. When empty, a timestamp and GUID name is generated. |
FileExtension |
Existing blobs are overwritten. The response file holds the blob name.
NativeRebexFtpUploadHandler#
Uploads the payload over SFTP or FTP. Needs a Rebex license.
| Property | Default | Notes |
|---|---|---|
Host (required), Username (required) |
||
Port |
22 for SFTP, 21 for FTP | |
Protocol |
sftp |
sftp or ftp with a password, or sftpssh with a private key. |
Password (secret) |
Required for sftp and ftp. The key passphrase for sftpssh. |
|
PrivateKey (secret) |
Required for sftpssh. A PEM key pasted onto one line is re-wrapped. |
|
TargetPath |
Remote directory. | |
FileNamePrefix |
Prepended as {prefix}_. |
|
DataEncoding |
utf8 |
base64 decodes the payload into bytes before uploading. |
The file is named after the exchange file, or a UTC timestamp when it has no name.
Receivers#
BatchSize caps the items taken per run and defaults to 50. ResponseEncoding is utf8 or base64; use base64 for binary content.
NativeHttpReceiver#
Calls an HTTP endpoint once per run and turns the JSON reply into items. Its properties match the HTTP handler's, with these differences: Verb defaults to get, there is no URL templating or multipart, Login posts UserName rather than Email, and there is one extra property.
| Property | Notes |
|---|---|
ArrayPath |
JSON path to the array of items. Without it, a root array is split into items and any other reply is a single item. |
The reply must be JSON, and a status of 400 or above fails the run. There is no pagination, items have no file name, and nothing is removed at the source.
NativeS3Receiver#
| Property | Notes |
|---|---|
AccessKeyId, SecretAccessKey (secret), ServiceUrl, BucketName |
Required. |
FolderName |
Key prefix. No / is added, so incoming also matches incoming-archive/. |
BatchSize, ResponseEncoding |
|
DeleteMovesFileTo |
When set, each processed object is copied under this prefix and then deleted. Otherwise it is deleted. |
NativeAzureBlobReceiver#
| Property | Notes |
|---|---|
ConnectionString (required, secret), ContainerName (required) |
|
FolderName |
Prefix, with a / added. |
BatchSize, ResponseEncoding, DeleteMovesFileTo |
As for S3. A blob that is already gone is skipped. |
NativeRebexFtpReceiver#
Reads files over SFTP or FTP. Needs a Rebex license. The connection properties match the FTP upload handler's.
| Property | Default | Notes |
|---|---|---|
TargetPath |
Directory to read. | |
BatchSize, ResponseEncoding |
||
DeleteMovesFileTo |
When set, processed files are moved into this directory. Otherwise they are deleted. | |
CheckFileExistence |
true |
Skip the delete quietly when the file is already gone. |
NativePop3Receiver and NativeRebexPop3Receiver#
Read email from a POP3 mailbox over implicit TLS on port 995. NativePop3Receiver uses MailKit. NativeRebexPop3Receiver uses Rebex and needs a license.
| Property | Notes |
|---|---|
Host, Username, Password (secret) |
Required. |
BatchSize, ResponseEncoding |
Each email becomes one item, named after its subject. When an email has attachments, only the first attachment is used. Otherwise its text body is used. Processed emails are deleted when the session closes.
Mappers#
| Id | Description |
|---|---|
NativeMapper |
Rules-based mapper for JSON and XML, with a visual editor |
NativeJSONMapper |
Legacy Scriban template mapper for JSON, offered only while a subscription still uses it |
See Mapping. Bitween has no native validators.
Custom adapters#
A custom adapter is a .NET console application that references SimplyWorks.Serverless.Sdk. This is the repository's sample handler.
using SW.PrimitiveTypes;
using SW.Serverless.Sdk;
class Program
{
static async Task Main(string[] args) => await Runner.Run(new Handler());
}
class Handler : IInfolinkHandler
{
public Handler()
{
// Declares a property named ContentType with a default value.
Runner.Expect("ContentType", "text/plain");
}
public Task<XchangeFile> Handle(XchangeFile xchangeFile)
{
var contentType = Runner.StartupValueOf("ContentType");
return Task.FromResult(xchangeFile);
}
}
- Declare properties in the constructor with
Runner.Expect. Overloads take a default value, whether the property is private, and a description. - Read values with
Runner.StartupValueOf(name). - A validator implements
IInfolinkValidatorand returnsnew InfolinkValidatorResult(errors), where no errors means valid. - A receiver implements
IInfolinkReceiver. Runner.CorrelationIdholds the exchange's correlation id.AdapterLoggerforwards log lines to Bitween.
| Sample project | Shows |
|---|---|
SW.Bitween.SampleHandler |
A handler with one property |
SW.Bitween.SampleMapper |
A mapper, which is a handler |
SW.Bitween.SampleValidator |
A validator using FluentValidation |
SW.Bitween.SampleConfigurableAdapter |
A test double that can delay, fail or return fixed data |
SW.Bitween.SampleResidentHandler |
A handler built as a resident adapter |
Installing a custom adapter#
Custom adapter ids follow the pattern infolink6.{kind}.{name}, where kind is handlers, receivers, mappers or validators. The adapter picker lists packages stored under these keys.
{Bitween:AdapterPath}/infolink6.{kind}.{name}
{Bitween:AdapterPath}/infolink6.{kind}.{name}/{major.minor.patch}
A trailing semantic version segment is treated as a version of the adapter. A package is a zip of the published console application. The integration tests upload packages with EntryAssembly and Hash metadata for the serverless runner. How the runner chooses among versions is decided inside SimplyWorks.Serverless and is not visible in this repository.
A custom adapter may run for Bitween:ServerlessCommandTimeout seconds, 300 by default.
To write a native adapter instead, see Development.