Upload & Editing
How the client writes ABAP objects back to a SAP system via ADT REST endpoints
Does the client support uploads?
PUT, POST, and
DELETE.
The relevant source files are src/api/objectcontents.ts, src/api/objectcreator.ts, src/api/activate.ts, src/api/transports.ts, and src/api/abapgit.ts.
Standard upload workflow
SAP ADT requires a stateful CSRF-protected session and an exclusive object lock before any source can be written. The four-step pattern is consistent across the entire codebase and is demonstrated in the integration tests (src/test/disruptive.test.ts:74):
- Acquire an exclusive lock on the object — returns a
LOCK_HANDLE - PUT the source (or XML payload) to the object's source URL, passing the lock handle as a query parameter
- Release the lock
- Activate (compile) the object by POSTing to
/sap/bc/adt/activation
// From src/test/disruptive.test.ts — real integration test helper
const writeobject = async (c: ADTClient, path: string, source: string, transport = "") => {
c.stateful = session_types.stateful // must be stateful
const handle = await c.lock(path) // step 1 — POST ?_action=LOCK
await c.setObjectSource( // step 2 — PUT source
path, source, handle.LOCK_HANDLE, transport
)
await c.unLock(path, handle.LOCK_HANDLE) // step 3 — POST ?_action=UNLOCK
const baseUrl = path.replace(/\/source|includes\/.*$/, "")
await c.activate(objectName, baseUrl) // step 4 — POST /sap/bc/adt/activation
}
Core upload API calls
setObjectSource — write ABAP / XML source
The primary write method. Sends source as a PUT request.
Detects XML vs plain-text and sets Content-Type accordingly.
src/api/objectcontents.ts:38
export async function setObjectSource(
h: AdtHTTP,
objectSourceUrl: string,
source: string,
lockHandle: string, // required — obtained from lock()
transport?: string // optional CTS transport number
) {
const qs: any = { lockHandle }
const ctype = source.match(/^<\?xml\s/i)
? "application/*"
: "text/plain; charset=utf-8"
if (transport) qs.corrNr = transport
await h.request(objectSourceUrl, {
body: source,
headers: { "content-type": ctype },
method: "PUT",
qs
})
}
| Parameter | Sent as | Notes |
|---|---|---|
lockHandle | Query string | Required; must pre-lock the object |
corrNr | Query string | CTS transport number (optional) |
source | Request body | Raw ABAP text or XML |
Content-Type | Header | Auto-detected: text/plain or application/* |
lock / unLock — exclusive object locking
Locks use the same object URL with a POST action parameter.
The returned AdtLock carries the handle and the associated
transport number. src/api/objectcontents.ts:60
// Lock — POST {objectUrl}?_action=LOCK&accessMode=MODIFY
export async function lock(h: AdtHTTP, objectUrl: string, accessMode = "MODIFY") {
const response = await h.request(objectUrl, {
method: "POST",
qs: { _action: "LOCK", accessMode }
})
return parse(response.body) as AdtLock // { LOCK_HANDLE, CORRNR, … }
}
// Unlock — POST {objectUrl}?_action=UNLOCK&lockHandle=…
export async function unLock(h: AdtHTTP, objectUrl: string, lockHandle: string) {
await h.request(objectUrl, {
method: "POST",
qs: { _action: "UNLOCK", lockHandle: encodeURIComponent(lockHandle) }
})
}
activate — compile objects
After writing, objects must be activated. The method accepts a single object or a batch array. src/api/activate.ts:58
// POST /sap/bc/adt/activation?method=activate&preauditRequested=true
// Body: XML list of adtcore:objectReference elements
const result = await c.activate(objectName, objectUrl)
// returns { success: boolean, messages: [], inactive: [] }
DDIC object writes
Beyond raw source, the client can serialise and write structured DDIC
objects — Domains and Data Elements — as XML payloads via PUT.
| Function | Method | Content-Type | Source |
|---|---|---|---|
setDomainProperties |
PUT | application/* |
objectcontents.ts:153 |
setDataElementProperties |
PUT | application/* |
objectcontents.ts:299 |
Both functions build an XML string from typed property objects
(DomainProperties, DataElementProperties) and send it with
the same lock-handle / transport query-string pattern as setObjectSource.
Object creation
New objects are created with a single POST — no prior lock required.
src/api/objectcreator.ts:305
await c.createObject({
objtype: "CLAS/OC", // object type key
name: "ZMY_CLASS",
parentName: "$TMP", // package
description: "My class",
transport: "NPLK900123" // optional — maps to corrNr
})
Supported object types include (non-exhaustive):
| Key | Type |
|---|---|
PROG/P | Program |
CLAS/OC | Class |
INTF/OI | Interface |
FUGR/F | Function group |
FUGR/FF | Function module |
DDLS/DF | CDS Data Definition |
TABL/DT | Transparent table |
DEVC/K | Package |
Transport (CTS) handling
Every write operation accepts an optional transport number passed as the
corrNr query parameter. The client also exposes a full transport
management API — see the Transports page.
| Function | Method | Endpoint |
|---|---|---|
transportInfo |
POST | /sap/bc/adt/cts/transportchecks |
createTransport |
POST | /sap/bc/adt/cts/transports |
transportRelease |
POST | /sap/bc/adt/cts/transportrequests/{nr}/newreleasejobs |
transportSetOwner |
PUT | /sap/bc/adt/cts/transportrequests/{nr} |
transportAddUser |
POST | /sap/bc/adt/cts/transportrequests/{nr}/tasks |
transportDelete |
DELETE | /sap/bc/adt/cts/transportrequests/{nr} |
setTransportsConfig |
PUT | Dynamic URI (ETag / If-Match protected) |
abapGit integration
The client supports the abapGit ADT extension for git-based object pushes. See the abapGit page for full details. src/api/abapgit.ts
| Function | Method | Notes |
|---|---|---|
createRepo |
POST | Links a package to a remote git URL |
pullRepo |
POST | Pulls objects from git into SAP |
pushRepo |
POST | Stages and pushes SAP objects to git |
checkRepo |
POST | Validates repo consistency |
switchRepoBranch |
POST | Switches / creates branches |
unlinkRepo |
DELETE | Removes the repo link |
// Stage selected objects and push to remote git
await c.pushRepo(repo, staging, gitUser, gitPassword)
// POST to repo.links[push_link].href with XML staging body
Session & authentication requirements
ValidateStateful(h) and will throw if the
session is not in stateful mode. A stateless session can only perform read operations.
// Enable stateful mode before any write
c.stateful = session_types.stateful
// After writes are done, drop the session cleanly
await c.dropSession()
The HTTP layer (src/AdtHTTP.ts) handles CSRF token
management automatically. The first GET with
X-CSRF-Token: Fetch seeds the token; all subsequent mutating requests
replay it in the request header.
Quick reference — HTTP methods by operation
| Operation | Method | Lock needed? | Transport param |
|---|---|---|---|
| Write source / DDIC XML | PUT | Yes | corrNr |
| Lock object | POST | — | — |
| Unlock object | POST | — | — |
| Create object | POST | No | corrNr |
| Activate object(s) | POST | No | — |
| Delete object | DELETE | Yes | corrNr |
| Create transport | POST | No | — |
| Release transport | POST | No | — |
| abapGit push | POST | No | Embedded in body |