Object Creation
Creating, validating, and deleting ABAP repository objects via ADT
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
// 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
| Field | Required | Notes |
|---|---|---|
objtype | Yes | Type ID string — see full table below |
name | Yes | Object name in uppercase. Max length varies by type |
parentName | Yes | Package name (e.g. $TMP) or function group name |
parentPath | Yes | ADT URL of the parent package or function group |
description | Yes | Short description, XML-entity-encoded automatically |
transport | No | CTS transport number — sent as corrNr query param |
responsible | No | Defaults to the logged-in username, uppercased |
language | No | Defaults to "EN" |
masterLanguage | No | Defaults to language |
masterSystem | No | Optional — 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 ID | Label | ADT endpoint | Max name len |
|---|---|---|---|
PROG/P | Program | /sap/bc/adt/programs/programs | 30 |
PROG/I | Include | /sap/bc/adt/programs/includes | 30 |
CLAS/OC | Class | /sap/bc/adt/oo/classes | 30 |
INTF/OI | Interface | /sap/bc/adt/oo/interfaces | 30 |
FUGR/F | Function group | /sap/bc/adt/functions/groups | 26 |
FUGR/FF | Function module | /sap/bc/adt/functions/groups/{group}/fmodules | 30 |
FUGR/I | Function group include | /sap/bc/adt/functions/groups/{group}/includes | 3 |
DDLS/DF | CDS Data Definition | /sap/bc/adt/ddic/ddl/sources | 30 |
DCLS/DL | CDS Access Control | /sap/bc/adt/acm/dcl/sources | 30 |
DDLX/EX | CDS Metadata Extension | /sap/bc/adt/ddic/ddlx/sources | 30 |
DDLA/ADF | CDS Annotation Definition | /sap/bc/adt/ddic/ddla/sources | 30 |
TABL/DT | Transparent table | /sap/bc/adt/ddic/tables | 16 |
TABL/DS | Structure | /sap/bc/adt/ddic/structures | 30 |
DTEL/DE | Data Element | /sap/bc/adt/ddic/dataelements | 30 |
DOMA/DD | Domain | /sap/bc/adt/ddic/domains | 30 |
SRVD/SRV | Service Definition | /sap/bc/adt/ddic/srvd/sources | 30 |
SRVB/SVB | Service Binding | /sap/bc/adt/businessservices/bindings | 26 |
DEVC/K | Package | /sap/bc/adt/packages | 30 |
AUTH | Authorization field | /sap/bc/adt/aps/iam/auth | 10 |
SUSO/B | Authorization object | /sap/bc/adt/aps/iam/suso | 10 |
MSAG/N | Message class | /sap/bc/adt/messageclass | 20 |
Type families and body builders
Internally, createBody() dispatches to one of three XML serialisers
based on the type. src/api/objectcreator.ts:218
| Builder | Used for | Key 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)
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}",
// }