Skip to main content

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

ConnectorBean name
(autocomplete @Ctrl Space)
Typical tasksWhen to use
REST@tsmRestClientGET / POST / PUT / PATCH / DELETE with JSON, XML, form‑data, file uploads, …Talking to another microservice, 3rd‑party SaaS, public APIs
SOAP@tsmSoapClientXML over text/xml, optional Envelope & Action headerLegacy SOAP stacks (banks, ERPs, public e‑Gov)
Database@tsmDatabaseClientSELECT, INSERT, UPDATE, DELETE via JDBCReading 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() (or update() 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

OptionREST / SOAPDBWhat 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

MethodWhen 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 defined tsm.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, then addMultipart('file', #bytes, 'invoice.pdf').
  • Binary downloadsresponseTypeByteArray() returns byte[].
  • SSL lab environmentsignoreSslErrors() trusts any certificate.
  • Non‑JSON APIsresponseTypeString() keeps the raw text.
  • Error scenarios – combine ignoreHttpErrors() with a custom responseType 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 to Map<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

AspectREST / SOAPDB
Part of microservice TX?✘ – independent✘ – auto‑commit
Rollback on SpEL error?– request already sent– statements executed
Recommended mitigationUse idempotent endpoints, retry mechanisms, or wrap changes in Saga patternLimit 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!  🚀