lumnik. / scenarios All scenarios · Français

The CRM's SOAP desk read service by service with nothing read twice

Goal Read an old CRM's SOAP services, each as it reads, without hammering it.

A 2000s CRM exposes its data through custom SOAP services. The password travels in the envelope.

manifests/10-actions.yaml
apiVersion: connectors.lumnik.io/v1kind: Connectormetadata:  name: scenario-siebel-a  connector_type: rest-generic  scopes: [crm]spec:  base_url: http://host.docker.internal:8097  endpoints:    # A custom business service: the actions of one request, one answer, no paging.    - id: actions      kind: soap      path: /eai_enu/start.swe?SWEExtSource=CustomUI&SWEExtCmd=Execute      soap_action: "document/http://siebel.com/CustomUI:getListActions"      header: |        <s1:SessionType xmlns:s1="http://siebel.com/webservices">None</s1:SessionType>        <s2:UsernameToken xmlns:s2="http://siebel.com/webservices">LAB_SVC</s2:UsernameToken>        <s3:PasswordText xmlns:s3="http://siebel.com/webservices">{{secret:SCENARIO_SIEBEL_A_PASSWORD}}</s3:PasswordText>      body: |        <ns2:getListActions_Input xmlns="http://crm.example.com/getListActions/Data" xmlns:ns2="http://siebel.com/CustomUI">          <getListActions>            <ListActions>              <cdTopOuverture>0</cdTopOuverture>              <dtDebutPeriode>01/01/1900</dtDebutPeriode>              <dtFinPeriode>12/31/9999</dtFinPeriode>              <origine>portail-svc-demandes</origine>              <idGRC>40012345</idGRC>              <idDemande>1-7QX2K4A</idDemande>            </ListActions>          </getListActions>        </ns2:getListActions_Input>      pagination:        kind: soap        page_size: 1000        response_path: "$.Body.getListActions_Output.ListActions.Action"      target:        kind: hybrid        table: ext.siebel_actions_a        unique_key: "$.idAction"        promote:          - { name: id_demande, path: "$.idDemande", type: text }          - { name: type_action, path: "$.typeAction", type: text }          - { name: statut, path: "$.statut", type: text }
manifests/20-opportunities.yaml
apiVersion: connectors.lumnik.io/v1kind: Connectormetadata:  name: scenario-siebel-opp-a  connector_type: rest-generic  scopes: [crm]spec:  base_url: http://host.docker.internal:8097  endpoints:    # A person's opportunities, paged by a 1-based page NUMBER; the page says whether it is the last.    - id: opportunities      kind: soap      path: /eai_enu/start.swe?SWEExtSource=CustomUI&SWEExtCmd=Execute      soap_action: "document/http://siebel.com/CustomUI:getListOpportunity"      header: |        <s1:SessionType xmlns:s1="http://siebel.com/webservices">None</s1:SessionType>        <s2:UsernameToken xmlns:s2="http://siebel.com/webservices">LAB_SVC</s2:UsernameToken>        <s3:PasswordText xmlns:s3="http://siebel.com/webservices">{{secret:SCENARIO_SIEBEL_A_PASSWORD}}</s3:PasswordText>      body: |        <cus:getListOpportunity_Input xmlns:cus="http://siebel.com/CustomUI" xmlns:data="http://crm.example.com/getOpportunity/Data">          <data:getOpportunity>            <data:Opportunity>              <data:refExtPersonne>40012345</data:refExtPersonne>              <data:cdTopStatutOpportunite>0</data:cdTopStatutOpportunite>              <data:origine>portail-svc-demandes</data:origine>              <data:page>{{page}}</data:page>            </data:Opportunity>          </data:getOpportunity>        </cus:getListOpportunity_Input>      pagination:        kind: soap        page_size: 3        last_page_path: "$.Body.getListOpportunity_Output.getOpportunity.lastpage"        response_path: "$.Body.getListOpportunity_Output.getOpportunity.Opportunity"      target:        kind: hybrid        table: ext.siebel_opportunities_a        unique_key: "$.idOpportunite"        promote:          - { name: libelle, path: "$.libelle", type: text }          - { name: statut, path: "$.statut", type: text }
crm-night1.json
{
 "actions": [
  {
   "idAction": "1-7QX3A01",
   "idDemande": "1-7QX2K4A",
   "dtAction": "10/14/2026 09:12:00",
   "typeAction": "Appel entrant",
   "statut": "Terminée",
   "auteur": "conseiller.07",
   "commentaire": "Client signale une facture en double"
  },
  {
   "idAction": "1-7QX3A02",
   "idDemande": "1-7QX2K4A",
   "dtAction": "10/14/2026 09:30:00",
   "typeAction": "Courrier",
   "statut": "Terminée",
   "auteur": "conseiller.07",
   "commentaire": "Accusé de réception envoyé"
  },

… 80 more lines

$ lm connector run scenario-siebel-opp-a
run scheduled: da227b07-7f92-4e1e-9368-5430196271c6

$ lm run get da227b07-7f92-4e1e-9368-5430196271c6
ID        CONNECTOR              ENDPOINT       STATUS     IN  OUT  SKIPPED  DEAD-LETTERS  STARTED           ENDED
da227b07  scenario-siebel-opp-a  opportunities  Completed  7   7    0        0             2026-11-03 16:10  2026-11-03 16:10
$ lm connector run scenario-siebel-a
run scheduled: f5be7458-f869-4a9f-8866-a461fee6c7e5

$ lm run get f5be7458-f869-4a9f-8866-a461fee6c7e5
ID        CONNECTOR          ENDPOINT  STATUS  IN  OUT  SKIPPED  DEAD-LETTERS  STARTED           ENDED
f5be7458  scenario-siebel-a  actions   Failed  0   0    0        0             2026-11-03 16:10  2026-11-03 16:10
✖ run failed: SOAP Fault: Invalid user name or password (SBL-UIF-00348)
crm-night2.json
{
 "actions": [
  {
   "idAction": "1-7QX3A01",
   "idDemande": "1-7QX2K4A",
   "dtAction": "10/14/2026 09:12:00",
   "typeAction": "Appel entrant",
   "statut": "Terminée",
   "auteur": "conseiller.07",
   "commentaire": "Client signale une facture en double"
  },
  {
   "idAction": "1-7QX3A02",
   "idDemande": "1-7QX2K4A",
   "dtAction": "10/14/2026 09:30:00",
   "typeAction": "Courrier",
   "statut": "Terminée",
   "auteur": "conseiller.07",
   "commentaire": "Accusé de réception envoyé"
  },

… 98 more lines

Limits

  • A SOAP read is one pass, in memory: lm run cancel only takes effect when the read ends.
  • The service is called for one case and one customer written in the manifest; one call per case of a list is not covered.
  • An incremental import's mark travels in the URL, not in the body: a service that filters by period in its body is read whole.
  • Simulated CRM; synthetic data.

CRM · SOAP

The CRM speaks SOAP.

Custom services, from the 2000s.

Simulated CRM, two services

The problem

Each service reads its own way.

  • One returns everything in one call.
  • Another pages by page number.

What is not changed

The CRM stays as it is.

  • Its services, its envelope.
  • A service account.

The trap

Hammering the CRM.

  • Reading the same page in a loop.
  • Retrying a refused password.

What had to be written

<data:page>{{page}}</data:page>

One line.

  • The page number, where the service expects it.
  • The stop on the last page.
  • An 84-line scenario, two manifests.

A refusal

The CRM's Fault, verbatim.

  • On the run, with its code.
  • A single attempt.

The limit

One request, one client, written in the manifest.

  • One call per item of a list is not covered.

Try it

The scenario replays locally.

Every step is published with its output.

This scenario runs on lumnik Open, free and open source.

Try lumnik Open →