Overview

Object creation in ADT is a single POST with an XML body — no lock is required. The URL and body structure vary by object type but follow a consistent pattern driven by the CreatableTypes map. src/api/objectcreator.ts:359

Unlike writes to existing objects, creation does not require a lock. However, it must be called on a stateless clone — calling it on a stateful session leaves the server in an inconsistent state. src/test/disruptive.test.ts:67
// From disruptive.test.ts — always use statelessClone for createObject
await c.statelessClone.createObject(options)

createObject

The main creation function. Looks up the object type in CreatableTypes, builds the XML body, and POSTs to the type's creation endpoint. src/api/objectcreator.ts:305

export async function createObject(
  h: AdtHTTP,
  options: NewObjectOptions | NewPackageOptions
) {
  const ot = CreatableTypes.get(options.objtype)   // look up type metadata
  const url = "/sap/bc/adt/" + sprintf(ot.creationPath, encodeURIComponent(options.parentName))
  options.responsible = (options.responsible || h.username).toUpperCase()
  const body = createBody(options, ot)          // XML body, varies by type
  const qs: any = {}
  if (options.transport) qs.corrNr = options.transport

  await h.request(url, {
    body,
    headers: { "Content-Type": "application/*" },
    method: "POST",
    qs
  })
}

NewObjectOptions

The base options interface used for all non-package object types. src/api/objectcreator.ts:79

FieldRequiredNotes
objtypeYesType ID string — see full table below
nameYesObject name in uppercase. Max length varies by type
parentNameYesPackage name (e.g. $TMP) or function group name
parentPathYesADT URL of the parent package or function group
descriptionYesShort description, XML-entity-encoded automatically
transportNoCTS transport number — sent as corrNr query param
responsibleNoDefaults to the logged-in username, uppercased
languageNoDefaults to "EN"
masterLanguageNoDefaults to language
masterSystemNoOptional — omitted from XML when empty

Supported object types

Every supported type is registered in the CreatableTypes map at the bottom of src/api/objectcreator.ts:373. Each entry defines the creation endpoint, the XML root element, and the max name length.

Type IDLabelADT endpointMax name len
PROG/PProgram/sap/bc/adt/programs/programs30
PROG/IInclude/sap/bc/adt/programs/includes30
CLAS/OCClass/sap/bc/adt/oo/classes30
INTF/OIInterface/sap/bc/adt/oo/interfaces30
FUGR/FFunction group/sap/bc/adt/functions/groups26
FUGR/FFFunction module/sap/bc/adt/functions/groups/{group}/fmodules30
FUGR/IFunction group include/sap/bc/adt/functions/groups/{group}/includes3
DDLS/DFCDS Data Definition/sap/bc/adt/ddic/ddl/sources30
DCLS/DLCDS Access Control/sap/bc/adt/acm/dcl/sources30
DDLX/EXCDS Metadata Extension/sap/bc/adt/ddic/ddlx/sources30
DDLA/ADFCDS Annotation Definition/sap/bc/adt/ddic/ddla/sources30
TABL/DTTransparent table/sap/bc/adt/ddic/tables16
TABL/DSStructure/sap/bc/adt/ddic/structures30
DTEL/DEData Element/sap/bc/adt/ddic/dataelements30
DOMA/DDDomain/sap/bc/adt/ddic/domains30
SRVD/SRVService Definition/sap/bc/adt/ddic/srvd/sources30
SRVB/SVBService Binding/sap/bc/adt/businessservices/bindings26
DEVC/KPackage/sap/bc/adt/packages30
AUTHAuthorization field/sap/bc/adt/aps/iam/auth10
SUSO/BAuthorization object/sap/bc/adt/aps/iam/suso10
MSAG/NMessage class/sap/bc/adt/messageclass20

Type families and body builders

Internally, createBody() dispatches to one of three XML serialisers based on the type. src/api/objectcreator.ts:218

BuilderUsed forKey difference
createBodySimple Most types Standard XML with adtcore:packageRef as child. Includes language fields.
createBodyFunc FUGR/FF, FUGR/I Uses adtcore:containerRef pointing to the parent function group instead of a package ref. No language fields.
createBodyPackage DEVC/K Requires extra fields: swcomp, transportLayer, packagetype. Uses NewPackageOptions.
createBodyBinding SRVB/SVB Embeds service definition reference and binding type. Requires NewBindingOptions.

Examples from tests

Program — PROG/P

src/test/disruptive.test.ts:142

await c.statelessClone.createObject({
  objtype:     "PROG/P",
  name:        "ZADTTEST_TEMPORARY",
  parentName:  "$TMP",
  parentPath:  "/sap/bc/adt/packages/$TMP",
  description: "temporary test program"
})
// POSTs to: /sap/bc/adt/programs/programs

Class — CLAS/OC

src/test/disruptive.test.ts:473

await c.createObject(
  "CLAS/OC",
  "ZADTFOOBARTESTTCINCL",
  "$TMP",
  "test with test classes include",
  "/sap/bc/adt/packages/$TMP"
)
// POSTs to: /sap/bc/adt/oo/classes

Interface — INTF/OI

src/test/disruptive.test.ts:178

await c.createObject(
  "INTF/OI",
  "YIF_ADTNPM_FOOBAR",
  "$TMP",
  "test object for ADT API",
  "/sap/bc/adt/packages/$TMP"
)
// POSTs to: /sap/bc/adt/oo/interfaces

Function group + module — FUGR/F and FUGR/FF

Function modules are child objects of a function group. The group must be created first; the module's parentName and parentPath reference the group, not the package. src/test/disruptive.test.ts:110

// 1. Create the function group first
await c.createObject({
  objtype:     "FUGR/F",
  name:        "Y_ADTNPM_FOOBAR",
  parentName:  "$TMP",
  parentPath:  "/sap/bc/adt/packages/$TMP",
  description: "test object for ADT API"
})
// POSTs to: /sap/bc/adt/functions/groups

// 2. Create a function module inside it
await c.createObject({
  objtype:     "FUGR/FF",
  name:        "Y_ADTNPM_FOOBARFM",
  parentName:  "Y_ADTNPM_FOOBAR",            // function group name
  parentPath:  "/sap/bc/adt/functions/groups/y_adtnpm_foobar",
  description: "test FM"
})
// POSTs to: /sap/bc/adt/functions/groups/y_adtnpm_foobar/fmodules

Package — DEVC/K

Packages require three extra fields and must use NewPackageOptions. src/test/disruptive.test.ts:329

await c.statelessClone.createObject({
  objtype:        "DEVC/K",
  name:           "$ADTAPIDUMMYPACKAGE",
  parentName:     "$TMP",
  parentPath:     "/sap/bc/adt/packages/$TMP",
  description:    "test Package creation",
  swcomp:         "LOCAL",         // software component
  transportLayer: "",              // empty = no transport layer
  packagetype:    "development"    // "development" | "structure" | "main"
} as NewPackageOptions)
// POSTs to: /sap/bc/adt/packages

CDS objects — DDLS/DF, DCLS/DL, DDLX/EX

src/test/disruptive.test.ts:282

// CDS Access Control
await c.statelessClone.createObject({
  objtype:     "DCLS/DL",
  name:        "ZADTTESTCDSACCON",
  parentName:  "$TMP",
  parentPath:  "/sap/bc/adt/packages/$TMP",
  description: "test CDS AC creation"
})
// POSTs to: /sap/bc/adt/acm/dcl/sources

// CDS Metadata Extension
await c.statelessClone.createObject({
  objtype:     "DDLX/EX",
  name:        "ZADTTESTCDSMETA",
  parentName:  "$TMP",
  parentPath:  "/sap/bc/adt/packages/$TMP",
  description: "test CDS AC creation"
})
// POSTs to: /sap/bc/adt/ddic/ddlx/sources

// CDS Data Definition
await c.statelessClone.createObject({
  objtype:     "DDLS/DF",
  name:        "ZADT_TEST_DD",
  parentName:  "$TMP",
  parentPath:  "/sap/bc/adt/packages/$TMP",
  description: "test CDS AC creation"
})
// POSTs to: /sap/bc/adt/ddic/ddl/sources

Validation before creation

Before creating an object, you can validate the proposed name and package against server-side rules (naming conventions, namespace checks, duplicate detection). src/api/objectcreator.ts:279

const result = await c.validateNewObject({
  objtype:     "PROG/P",
  objname:     "ZADTTEST_TEMPORARY",
  packagename: "$TMP",
  description: "temporary test program"
})
// result: { success: boolean, SEVERITY?: string, SHORT_TEXT?: string }
// throws AdtException directly if SEVERITY === "ERROR"

Validation POSTs to the type's validationPath with the options as query parameters. The disruptive tests always validate before creating: src/test/disruptive.test.ts:64

const vresult = await c.validateNewObject(validateOptions)
expect(vresult.success).toBeTruthy()
await c.statelessClone.createObject(options)
Group types (FUGR/FF, FUGR/I) cannot be passed to validateNewObject directly — the type system enforces this. The test helper skips validation for group types via if (isGroupType(options.objtype)) return. src/test/disruptive.test.ts:48

objectPath helper

Derives the ADT URL for an object from its type and name without making any HTTP request. Useful for constructing lock/write/delete URLs immediately after creation. src/api/objectcreator.ts:250

// From NewObjectOptions
const url = objectPath({ objtype: "CLAS/OC", name: "ZMYCLASS", parentName: "$TMP", ... })
// → "/sap/bc/adt/oo/classes/ZMYCLASS"

// Shorthand overloads
objectPath("DEVC/K", "$MYPKG")
// → "/sap/bc/adt/packages/$MYPKG"

objectPath("FUGR/FF", "MY_FM", "MY_GROUP")
// → "/sap/bc/adt/functions/groups/MY_GROUP/fmodules/MY_FM"

Adding a test class include

ABAP classes can have a dedicated unit-test include. This is a separate creation step after the class exists — it requires a lock on the class and posts to the class's /includes sub-resource. src/api/objectcreator.ts:331

// From disruptive.test.ts:481
c.stateful = session_types.stateful
const lock = await c.lock(clasurl)
await c.createTestInclude(clas, lock.LOCK_HANDLE)
// POST /sap/bc/adt/oo/classes/{clas}/includes
// Body: XML with class:includeType="testclasses"

// Verify it was created by reading the source
const source = await c.getObjectSource(clasurl + "/includes/testclasses")
createTestInclude is the only creation call that requires a lock. The lock handle is passed as a query parameter alongside the optional transport number. src/api/objectcreator.ts:346

Deletion

Deleting an object requires locking it first, then sending a DELETE request with the lock handle. The transport is optional. src/api/delete.ts:34

// Pattern used throughout disruptive.test.ts
c.stateful = session_types.stateful
const handle = await c.lock(objectUrl)
await c.deleteObject(objectUrl, handle.LOCK_HANDLE)
await c.dropSession()

In the tests this is wrapped in a deleteObj helper that swallows errors when the object doesn't exist — useful for cleanup before and after test runs. src/test/disruptive.test.ts:29

async function deleteObj(object: string, c: ADTClient, rethrow = false) {
  try {
    c.stateful = session_types.stateful
    const handle = await c.lock(object)
    await c.deleteObject(object, handle.LOCK_HANDLE)
  } catch (e) {
    if (rethrow) throw e   // rethrow=true when deletion must succeed
  }
  await c.dropSession()
}

Full lifecycle example

The write_program test demonstrates the complete lifecycle from creation through write, read, and deletion in one flow. src/test/disruptive.test.ts:133

const name = "zadttest_temporary"
const path = "/sap/bc/adt/programs/programs/" + name
const main = path + "/source/main"
const source = `Report ${name}.\nwrite:/ 'Hello,World!'.`

// 1. Delete any leftover from a previous run
await deleteObj(path, c)

// 2. Create
await c.createObject({ objtype: "PROG/P", name, parentName: "$TMP",
  parentPath: "/sap/bc/adt/packages/$TMP", description: "temporary test program" })

// 3. Lock and write source
c.stateful = session_types.stateful
const handle = await c.lock(path)
await c.setObjectSource(main, source, handle.LOCK_HANDLE)

// 4. Read back to verify
const newsource = await c.getObjectSource(main)

// 5. Delete (reuses the existing lock handle)
await c.deleteObject(path, handle.LOCK_HANDLE)
await c.unLock(path, handle.LOCK_HANDLE)
await c.dropSession()

Querying available types from the server

The static CreatableTypes map covers known types, but you can also query the live system for everything it supports — including capabilities per type. src/api/objectcreator.ts:234

const types = await c.loadTypes()
// POST /sap/bc/adt/repository/typestructure
// returns ObjectType[] — includes CAPABILITIES, CATEGORY, URI_TEMPLATE, max lengths

// Each ObjectType looks like:
// {
//   OBJECT_TYPE: "PROG/P",
//   OBJECT_TYPE_LABEL: "Program",
//   OBJNAME_MAXLENGTH: 30,
//   CAPABILITIES: ["create", "delete", "display", ...],
//   URI_TEMPLATE: "/sap/bc/adt/programs/programs/{name}",
// }