Does the client support uploads?

Yes — the client has full write support. It can create, edit, delete, and activate ABAP objects on a live SAP system, and also push/pull via abapGit. All write operations go through standard ADT REST endpoints using 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):

  1. Acquire an exclusive lock on the object — returns a LOCK_HANDLE
  2. PUT the source (or XML payload) to the object's source URL, passing the lock handle as a query parameter
  3. Release the lock
  4. 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
  })
}
ParameterSent asNotes
lockHandleQuery stringRequired; must pre-lock the object
corrNrQuery stringCTS transport number (optional)
sourceRequest bodyRaw ABAP text or XML
Content-TypeHeaderAuto-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.

FunctionMethodContent-TypeSource
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):

KeyType
PROG/PProgram
CLAS/OCClass
INTF/OIInterface
FUGR/FFunction group
FUGR/FFFunction module
DDLS/DFCDS Data Definition
TABL/DTTransparent table
DEVC/KPackage

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.

FunctionMethodEndpoint
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

FunctionMethodNotes
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

All write operations call 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

OperationMethodLock needed?Transport param
Write source / DDIC XMLPUTYescorrNr
Lock objectPOST
Unlock objectPOST
Create objectPOSTNocorrNr
Activate object(s)POSTNo
Delete objectDELETEYescorrNr
Create transportPOSTNo
Release transportPOSTNo
abapGit pushPOSTNoEmbedded in body