SpEL Connectors
Need to call a public REST API, consume a legacy SOAP service or run a quick SQL query right from a SpEL expression? Connectors have you covered. They are fluent wrapper beans that let you build, tune and execute external calls without leaving SpEL.
Integration Connectors vs. Generic Connectors
The tSM Platform ships with ready‑made Integration Connectors – opinionated adapters for well‑known SaaS platforms, public registers and network‑automation stacks such as Google Chat, Microsoft Teams, Atlassian Jira, Netbox, vendor NMSes, Netconf, Ansible, Terraform and many more. Each comes with built‑in business logic, validation, retries, exception handling and rich logging, so you can drop them into your processes with zero boilerplate.
Need something bespoke or want full protocol control? That’s where the Generic Connectors on this page come in. They expose low‑level yet ergonomic access to REST & SOAP endpoints, databases, message buses, SSH, SNMP and other protocols – all orchestrated through the same powerful tSM Expression Language.
1 Essentials in a Nutshell
1.1 Three flavours of connectors
Connector | Bean name (autocomplete @ → Ctrl Space) | Typical tasks | When to use |
---|---|---|---|
REST | @tsmRestClient | GET / POST / PUT / PATCH / DELETE with JSON, XML, form‑data, file uploads, … | Talking to another microservice, 3rd‑party SaaS, public APIs |
SOAP | @tsmSoapClient | XML over text/xml , optional Envelope & Action header | Legacy SOAP stacks (banks, ERPs, public e‑Gov) |
Database | @tsmDatabaseClient | SELECT , INSERT , UPDATE , DELETE via JDBC | Reading or updating data that still lives in an external DB |
All three share the same builder style:
@bean.method(args …).option(x).option(y).execute()
- Chain as many options as you like.
- Nothing is sent until you call
execute()
(orupdate()
for SQL writes).
1.2 30‑second cheat‑sheet
// ❶ Quick JSON GET
@tsmRestClient.get('https://api.acme.com/v1/users')
.requestParams({'active':'true'})
.timeout(5_000)
.execute().body
// ❷ POST with Bearer auth & file upload
@tsmRestClient.post('docs.acme.local','/upload')
.addHeader('Authorization','Bearer ' + #token)
.addMultipart('file', #pdfBytes, 'contract.pdf')
.execute()
// ❸ SOAP call from template
@tsmSoapClient.url('https://legacy.bank.com/ws')
.fromTemplate('Bank.PaymentOrder', {'amount':123.45, 'iban':#iban})
.execute().body
// ❹ One‑liner SQL lookup
@tsmDatabaseClient.db('tsm')
.query('select name from crm_customer where id=:id')
.param('id', #customerId)
.limit(1)
.execute()[0]?.name
// ❺ Ad‑hoc JDBC update (auto‑commit)
@tsmDatabaseClient.db('jdbc:postgresql://host/db','user','pwd')
.update('update crm_account set vip=true where id=:id')
.param('id', #accountId)
.update()
1.3 Frequently used options
Option | REST / SOAP | DB | What it does |
---|---|---|---|
headers(map) / addHeader(k,v) | ✔︎ | – | Custom HTTP headers |
basicAuth(u,p) | ✔︎ | – | Adds Authorization: Basic … |
timeout(ms) (or timeout(conn,read) ) | ✔︎ | – | Per‑request time‑out(s) |
ignoreHttpErrors() | ✔︎ | – | Treat non‑2xx as normal response |
ignoreSslErrors() | ✔︎ | – | Trust every certificate (dev only!) |
responseTypeByteArray() / responseType(Class) | ✔︎ | – | Control deserialisation of the body |
addMultipart(name,data[,filename]) | ✔︎ | – | Easy file uploads |
log(typeCode, ownerType, ownerId) | ✔︎ | ✔︎ | Write a Business Log entry for the call |
param(name,val) / params(map) | – | ✔︎ | Named SQL parameters |
limit(n) | – | ✔︎ | Safety cap, defaults to 1 000 |
Tip – Ctrl Space suggests every method and shows inline docs right in the console.
2 REST Client (@tsmRestClient
)
2.1 Quick reference of request builders
Method | When to pick |
---|---|
get(uri) | Read only |
post(uri) | Create resources or Invoke actions |
put(uri) | Full update |
patch(uri) | Partial update |
delete(uri) | Remove |
Each builder returns a Request object exposing the full option set:
@tsmRestClient.post('https://api.acme.com/orders')
.basicAuth('user','secret')
.body({'customerId':#id,'lines':#items})
.timeout(10_000)
.log('API_CALL','ORDER',#id)
.execute()
2.1.1 Building the target URL
- Absolute URL –
'https://sales.acme.com/v2/customers'
→ discouraged for production – use only for quick experiments. - tSM microservice –
'tsm-invoice/api/v1/invoices'
→ resolves via service‑discovery inside the cluster. - Named connector –
'sales-api' , '/orders'
once you have definedtsm.connector.rest.sales-api.url=https://sales.acme.dev/api
in application.yml.
Best practice – always go through a named connector. This keeps credentials, time‑outs and SSL rules in one place, makes endpoint changes environment‑agnostic and lets Ops rotate secrets without touching scripts.
2.1.2 Defining connectors in application.yml
# src/main/resources/application.yml (or externalised Config Server)
tsm:
connector:
# REST example -------------------------------------------
rest:
sales-api:
url: https://sales.acme.dev/api # Base URL
username: demo
password: s3cr3t
headers:
Accept: application/json
connect-timeout: 30000 # ms
read-timeout: 30000
# SOAP example -------------------------------------------
soap:
payment-gw:
url: https://gw.bank.demo/soap
username: paybot
password: changeMe!
ignore-ssl-errors: true # Dev only
# Database example ---------------------------------------
database:
legacy-ods:
url: jdbc:postgresql://legacy-db.demo:5432/ODS
username: legacy
password: changeme
maximum-pool-size: 10
connection-timeout: 30000
Use it like →
@tsmRestClient.post('sales-api','/orders').body(#dto).execute()
@tsmSoapClient.url('payment-gw','/PaymentService').fromTemplate('Bank.Pay',#params).execute()
@tsmDatabaseClient.db('legacy-ods').query('select 1').execute()
2.2 Advanced features Advanced features
- File uploads – call
contentTypeMultipart()
first, thenaddMultipart('file', #bytes, 'invoice.pdf')
. - Binary downloads –
responseTypeByteArray()
returnsbyte[]
. - SSL lab environments –
ignoreSslErrors()
trusts any certificate. - Non‑JSON APIs –
responseTypeString()
keeps the raw text. - Error scenarios – combine
ignoreHttpErrors()
with a customresponseType
to parse HTML or vendor‑specific XML.
3 SOAP Client (@tsmSoapClient
)
3.1 Anatomy of a SOAP call
@tsmSoapClient.url('bank-payments','/ws') // named connector
.soapAction('urn:SubmitPayment') // optional
.xml('<pay:SubmitPayment>…</pay:SubmitPayment>')
.addEnvelope(true) // default is true
.basicAuth('api','***')
.timeout(15_000)
.execute().body
Skip the envelope – set addEnvelope(false)
when your XML already contains <soapenv:Envelope>
.
3.2 Templating with Output Management
For large XMLs keep them in an OM template and supply just parameters:
@tsmSoapClient.url('https://bank.local/ws/payment')
.fromTemplate('Bank.PaymentOrder', {'iban':#iban,'amount':#sum})
.execute()
Behind the scenes the template is rendered, optionally wrapped in an envelope and posted with Content‑Type: text/xml
.
4 Database Client (@tsmDatabaseClient
)
4.1 Opening a connection
// Pre‑configured pool (recommended)
#db = @tsmDatabaseClient.db('tsm')
// Ad‑hoc JDBC URL (no pooling, auto‑commit)
#db = @tsmDatabaseClient.db('jdbc:postgresql://host/db','user','pwd')
tsm.connector.database.<name>
in application.yml defines the pools (URL, user, pool size, …).
4.2 Running queries
#customers = #db.query('select * from crm_customer where code in (:codes)')
.param('codes', #codeList)
.limit(500)
.execute() // → List<Map<String,Any?>>
- Named parameters
:param
keep SQL readable. - Result mapping – primitive columns are returned as Kotlin/Java types,
jsonb
columns deserialise toMap<String,Any?>
, arrays become ordinary arrays. - Safety first – any query without its own
limit
is automatically capped to 1000 rows (configurable via.limit(n)
).
4.2.1 Updates
rows = #db.update('update crm_contract set status=:s where id=:id')
.params({'s':'CANCELLED','id':#cid})
.update() // → affected row count
Updates commit immediately (they are not tied to the surrounding microservice transaction).
5 Transactions, Logging & Best Practices
Aspect | REST / SOAP | DB |
---|---|---|
Part of microservice TX? | ✘ – independent | ✘ – auto‑commit |
Rollback on SpEL error? | – request already sent | – statements executed |
Recommended mitigation | Use idempotent endpoints, retry mechanisms, or wrap changes in Saga pattern | Limit to reads whenever possible |
5.1 Business Logs
Every connector supports the shared log()
helper:
.log('EXT_CALL', 'ORDER', #order.id)
A nice timeline of external calls is then visible in any form that contains the Business Log Widget.
6 Autocomplete & Reference
@tsmRestClient
,@tsmSoapClient
,@tsmDatabaseClient
– type@tsm
+ Ctrl Space to list them.- After a builder call (
.get(
,.url(
,.query(
) hit Ctrl Space again to see all available options with their Javadoc. - Use
spel-all-methods
Swagger endpoint for a exhaustive method list.
Happy integrating – and remember: keep connector calls thin, idempotent and well‑logged, so the rest of your automation stays predictable! 🚀