@aplinkosministerija/dbsis-rest
v0.2.1
Published
REST-style client and HTTP gateway for the Lithuanian DBSIS (Dokumentų valdymo bendroji informacinė sistema) SOAP API. Wraps WS-Security UsernameToken auth, SOAP envelope construction, MTOM-friendly base64 attachments and JSON↔XML mapping so DBSIS web ser
Maintainers
Readme
dbsis-rest
REST-style Node.js client and HTTP gateway for the Lithuanian DBSIS (Dokumentų valdymo bendroji informacinė sistema) SOAP API.
DBSIS exposes ~85 SOAP operations across 11 web services (RDODocumentWS, OrgStructWS, ClassifierWS, JournalWS, OfficeCaseWS, TemplateWS, TDODocumentWS, …). Calling them directly requires:
- hand-crafted SOAP 1.1 envelopes with WS-Security UsernameToken authentication,
xsi:typeannotations on every Map entry value so the server picks the right Java class,- careful namespace bookkeeping (
org:,cls:,jrl:…), - MTOM-style base64 attachments,
- parsing the SOAP response into something usable.
This package handles all of the above and lets you call any operation as a JS function or as a JSON HTTP request:
const { createClient } = require('@aplinkosministerija/dbsis-rest');
const dbsis = createClient({
baseUrl: process.env.DBSIS_BASE_URL, // https://dbsis.lt/dbsis
username: process.env.DBSIS_USERNAME,
password: process.env.DBSIS_PASSWORD,
});
const { documentInfo } = await dbsis.documents.createDocumentFromTemplate({
documentFromTemplateParam: {
docAttributes: {
entry: [
{ key: 'title', value: { $type: 'xs:string', $text: 'My title' } },
{ key: 'receivers', value: {
$type: 'org:OrgNodeListParam',
orgNode: { $type: 'org:OrgNodeCodeParam', code: '188774822' },
}},
],
},
templateParam: { oid: 'inc1.188774822' },
register: false,
project: false,
},
});
console.log(documentInfo.oid); // -> "c89099f0bec611e593e2d8d76fa79d4c"Or run the bundled REST gateway and call each operation as JSON:
DBSIS_BASE_URL=https://dbsis.lt/dbsis \
DBSIS_USERNAME=integration_user \
DBSIS_PASSWORD=... \
npx dbsis-rest serve
curl -X POST http://localhost:3000/api/documents/getDocument \
-H 'content-type: application/json' \
-d '{"getDocumentParam":{"docOid":"c89099f0..."}}'The gateway and the JS client share the same operation catalogue, so anything documented in the DBSIS spec can be invoked uniformly.
Installation
npm install @aplinkosministerija/dbsis-restNode.js ≥ 18 is required.
Configuration
Configuration is read from environment variables only — credentials are never hard-coded or sent over HTTP from the gateway. Copy .env.example to .env and fill in real values:
| Variable | Required | Notes |
| --- | --- | --- |
| DBSIS_BASE_URL | yes | e.g. https://dbsis.lt/dbsis. The package appends the per-service path (/ws-cxf/RDODocumentWS, /ws-cxf/OrgStructWS, …) automatically — see Service URL mapping below. |
| DBSIS_USERNAME | yes | Dedicated integration user as recommended by the DBSIS team. |
| DBSIS_PASSWORD | yes | The password is sent in WS-Security UsernameToken / PasswordText form, so HTTPS is mandatory. |
| DBSIS_TIMEOUT_MS | no | HTTP timeout, default 60 000 ms (matches DBSIS server-side timeout). |
| PORT | no | Gateway port, default 3000. |
| GATEWAY_TOKEN | no | If set, the REST gateway requires Authorization: Bearer <token> on every request. |
Recommended: do not check .env into git. The package ships with a .gitignore that excludes it.
Service URL mapping
The live Apache CXF / Avilys DBSIS deployment exposes each SOAP service under
<base>/ws-cxf/<ServiceName> (verified against the running endpoints, e.g.
<base>/ws-cxf/RDODocumentWS?wsdl). The package uses these defaults — override
them via the servicePaths constructor option if your installation differs.
| Alias | Prefix | Path | DBSIS service |
| --- | --- | --- | --- |
| documents | rdod | /ws-cxf/RDODocumentWS | RDODocumentWS — registered documents |
| tasks | tdod | /ws-cxf/TDODocumentWS | TDODocumentWS — tasks |
| contracts | cdod | /ws-cxf/CDODocumentWS | Contracts (sutartys) |
| subst | subs | /ws-cxf/SubstDocumentWS | SubstDocumentWS — pavadavimai |
| org | org | /ws-cxf/OrgStructWS | OrgStructWS |
| classifiers | cls | /ws-cxf/ClassifierWS | ClassifierWS |
| journals | jrl | /ws-cxf/JournalWS | JournalWS — registers |
| officeCases | ofc | /ws-cxf/OfficeCaseWS | OfficeCaseWS — bylos |
| templates | tpl | /ws-cxf/TemplateWS | TemplateWS |
| documentSorts | dso | /ws-cxf/DocumentSortWS | DocumentSortWS |
| linkTypes | lnk | /ws-cxf/LinkTypeWS | LinkTypeWS |
MTOM/XOP responses. The CXF endpoints return responses as
multipart/related(MTOM).parseSoapResponsetransparently extracts the SOAP envelope from the multipart wrapper, so callers get plain JSON as usual.SOAPAction. The endpoints dispatch by the SOAP body element and register their operations with an empty
SOAPAction; the client sends""by default (override per call viasoapAction).
Programmatic API
const { createClient } = require('@aplinkosministerija/dbsis-rest');
const dbsis = createClient({ baseUrl, username, password });
// Each service exposes one async method per documented SOAP operation:
await dbsis.documents.getDocument({ getDocumentParam: { docOid: '...' } });
await dbsis.org.getOrgUnit({ orgName: 'org.lt.root' });
await dbsis.classifiers.getClsEntryList({ getClsEntryListParam: { className: 'clsDocPrivacy' } });
// Operation discovery:
dbsis.documents.$operations; // [ 'createDocumentFromTemplate', 'getDocument', ... ]
dbsis.documents.$invoke(name, p); // dynamic dispatch by string name
// Low-level escape hatch — call any SOAP operation, even one not in the
// catalogue, on any service prefix:
await dbsis.invoke({
servicePrefix: 'rdod',
operation: 'getDocument',
params: { getDocumentParam: { docOid: '...' } },
extraNamespaces: ['doc'],
});Building request payloads
The DBSIS SOAP body uses two non-trivial XML constructs — Map entries with typed values, and namespaced child elements. The package exposes them through three special object keys:
| Key | Effect |
| --- | --- |
| $type | Adds xsi:type="<value>" to the element. Use a prefix that matches a known namespace (xs:string, org:OrgNodeListParam, …). The relevant xmlns: declaration is added to the envelope automatically. |
| $text | Inline text content. Useful when you also need attributes. |
| $attrs | Plain XML attributes (e.g. wsu:Id). |
Arrays produce repeating sibling elements — exactly what the DBSIS entry/item/receivers lists need:
{
docAttributes: {
entry: [
{ key: 'title', value: { $type: 'xs:string', $text: 'Hello' } },
{ key: 'note', value: { $type: 'xs:string', $text: 'World' } },
],
},
}Buffer values are automatically base64-encoded — useful when uploading ADOC/PDF attachments via bodyAttachment.content.
null becomes xsi:nil="true". Dates become ISO-8601 strings.
Recipe: create an outgoing document (RDO_OUT) project with a PDF
A complete, working createDocumentFromTemplate that uploads a plain PDF as the document body and wires the approval/signing chain. This is the shape that actually works against a live Avilys tenant — see Gotchas below for the non-obvious parts.
// 1. Create the sending-document PROJECT from a template, with the PDF inline.
const { documentInfo } = await dbsis.documents.createDocumentFromTemplate({
documentFromTemplateParam: {
docAttributes: {
entry: [
{ key: 'title', value: { $type: 'xs:string', $text: 'Leidimas Nr. 002-G-2026' } },
// isElectro=false for a plain PDF body — `true` demands an ADOC/PDF-LT
// electronic *package*, not a plain application/pdf attachment.
{ key: 'isElectro', value: { $type: 'xs:boolean', $text: 'false' } },
{ key: 'senders', value: { $type: 'org:OrgNodeListParam', orgNode: { $type: 'org:OrgNodeParam', orgName: 'org.unit.…' } } },
{ key: 'receivers', value: { $type: 'org:OrgNodeListParam', orgNode: { $type: 'org:OrgNodeParam', orgName: 'org.unit.…' } } },
{ key: 'preparedBy', value: { $type: 'org:OrgNodeListParam', orgNode: { $type: 'org:OrgNodeParam', orgName: 'org.import…' } } },
{ key: 'draftJournal', value: { $type: 'jrl:JournalParam', oid: '…' } },
// draftOfficeCase is a SINGLE OfficeCaseParam{oid} — NOT an OfficeCaseListParam.
{ key: 'draftOfficeCase', value: { $type: 'ofc:OfficeCaseParam', oid: '…' } },
{ key: 'documentProcess', value: { $type: 'xs:string', $text: '…' } },
{ key: 'sort', value: { $type: 'cls:ClsEntryParam', oid: '…' } },
{ key: 'nature', value: { $type: 'cls:ClsEntryParam', clsid: 'clsNature.PP' } },
],
},
// The PDF is the document BODY — an AttachmentActionParam via `bodyAttachment`
// (inherited from ModifyDocumentParam). There is NO `eDocumentFile` field.
bodyAttachment: {
type: 'clsDHSAttaType.MAIN',
action: 'add',
title: 'leidimas.pdf',
content: pdfBuffer, // Buffer → inlined as base64 automatically
contentType: 'application/pdf',
customFields: {},
},
templateParam: { oid: '<templateOid>' },
register: false, // false = create a PROJECT (draftas), not registered
project: true,
},
});
const docOid = documentInfo.oid;
// 2. Wire the dynamic review/sign chain, then start the workflow.
await dbsis.documents.$invoke('modifyApprovalSchema', {
modifySchemaParam: { docOid, executors: [{ $type: 'org:OrgNodeParam', orgName: '…' }] },
});
await dbsis.documents.$invoke('modifySigningSchema', {
modifySchemaParam: { docOid, executors: [{ $type: 'org:OrgNodeParam', orgName: '…' }] },
});
await dbsis.documents.$invoke('markVersionReady', { markVersionReadyParam: { docOid } });Gotchas (hard-won against a live tenant)
- DBSIS returns detail-less internal errors (
GS-30000,GS-32001) on a malformed request — a generic "Nenumatyta vidinė sistemos klaida" with an empty<detail>/body, no field-level hint. Two ways to localise the offending field:- Drop the attachment and send metadata only — a missing main body surfaces a meaningful error ("Elektroninio dokumento turinyje turi būti pagrindinis dokumentas") that tells you the create path is otherwise fine.
getDocumentafter a "successful" create and inspect how each attribute was actually stored — e.g. a byla stored asxs:stringinstead of an office-case reference means it didn't resolve.
- There is no
eDocumentFile. The PDF/ADOC body isbodyAttachment(a single or array ofAttachmentActionParam). - There is no
OrgNodeNameParam. Reference org nodes withOrgNodeParam+orgName(orOrgNodeCodeParam+code). draftOfficeCaseis a singleOfficeCaseParam{ oid }, not anOfficeCaseListParam. Wrapping it makes the byla store as a raw string ("Lauko skaitymo klaida").isElectro: truerequires a real electronic package (ADOC/PDF-LT). For a plainapplication/pdfbody useisElectro: false(Lentelė 59:project=true+isElectro=false⇒ a plain outgoing-document project).sort/documentProcess/natureare valid createdocAttributesfor tenants whose template needs them, even though the spec's Lentelė 51 lists only a minimal set (its note allows config-specific extras).- Attachment
contentworks inline (the lib base64-encodes theBuffer); MTOM is only used on the response side. - Signing chain is set after create, via
modifyApprovalSchema(derintojai) /modifySigningSchema(pasirašantys) +markVersionReady— not as create-timedocAttributes.
Errors
Any SOAP Fault is thrown as SoapError (extends Error):
const { SoapError } = require('@aplinkosministerija/dbsis-rest');
try { ... }
catch (err) {
if (err instanceof SoapError) {
console.error(err.message); // faultstring
console.error(err.faultCode); // optional
console.error(err.detail); // optional <detail> contents
}
}HTTP-level failures (timeout, 5xx without a SOAP body, etc.) also surface as SoapError with a status and responseBody.
REST gateway
npx dbsis-rest serve
# or
node scripts/cli.js serve| Method | Path | Body | Description |
| --- | --- | --- | --- |
| GET | /health | — | { ok: true } |
| GET | /api/operations | — | Machine-readable catalogue of every registered operation. |
| POST | /api/<service>/<operation> | JSON | Same params object as the JS API. Returns { ok: true, result: ... } or { ok: false, error, soap }. |
The <service> segment accepts both the alias (documents) and the short prefix (rdod).
If GATEWAY_TOKEN is set, every request must include Authorization: Bearer <token>.
Catalogue
Run node scripts/cli.js list to print every supported operation grouped by service — same set the gateway exposes.
Receiving DBSIS callbacks (receiveDhsEvent, receiveDhsTaskEvent)
DBSIS calls your system back when a document/task changes state. The taikymo aprašymas (§3.2) and the spec (§2.18) describe the expected ReceiveDhsEventParam / ReceiveDhsTaskEventParam payload and the ReceiveDhsEventResult shape you must return.
This package does not run a SOAP server for you — that is intentional, because the URL, port, certificates and side-effects belong to your application. A complete reference example using only the standard library is in examples/receive-dhs-event-server.js (forthcoming): parse the envelope with parseSoapResponse, do your work, and reply with a SOAP envelope containing ReceiveDhsEventResult (statusCode, errorMessage, persistentError).
Development
npm install
npm test # node:test, no external test runner
npm run start # runs `dbsis-rest serve` against env varsCI/CD
GitHub Actions workflows mirror the conventions used by other Ministry repos (see biip-tools):
| Workflow | Trigger | Purpose |
| --- | --- | --- |
| ci.yml | push / PR to main | npm test on Node 18/20/22, npm audit, actionlint |
| codeql.yml | push / PR to main | Code scanning — skipped automatically on private repos (requires GHAS) |
| publish.yml | tag X.Y.Z push, GitHub Release, or workflow_dispatch | Test then npm publish --access public --provenance |
| dependabot.yml | daily | Security updates only (open-pull-requests-limit: 0) |
Releasing a new version:
# bump in package.json, e.g. 0.1.0 -> 0.1.1
git tag 0.1.1
git push origin 0.1.1
# publish workflow runs automaticallyThe publish job needs a repo or org secret named NPM_REGISTRY_TOKEN — same
name biip-tools uses, so an org-level secret will work without per-repo setup.
Mapping back to the spec
Every operation in src/services/operations.js has a docSection field that points to the chapter in DBSIS_SOAP_API_20250114_v3.0.docx. If a method is missing or its body shape is unclear, that is the place to look first.
Security notes
- Credentials never leave
process.env. The REST gateway never accepts SOAP credentials in the request body. If you expose the gateway externally, setGATEWAY_TOKENand front it with TLS. - WS-Security UsernameToken sends the password as
PasswordText. DBSIS itself recommends running over HTTPS — do not call the SOAP endpoint over plain HTTP. - Templates, sender/receiver codes, OIDs etc. are not secrets in themselves but identify documents in your tenant. Treat the JSON request bodies you log accordingly.
License
MIT.
