Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions packages/adt-contracts/src/adt/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export * from './programs';
export * from './functions';
export * from './ddic';
export * from './system';
export * from './rap';

/**
* Complete ADT Contract
Expand All @@ -33,6 +34,14 @@ import {
import { functionsContract, type FunctionsContract } from './functions';
import { ddicContract, type DdicContract } from './ddic';
import { systemContract, type SystemContract } from './system';
import {
behavdeftContract,
ddlsContract,
generatorContract,
type BehavdeftContract,
type DdlsContract,
type GeneratorContract,
} from './rap';

/**
* Explicit type to avoid TS7056 "inferred type exceeds maximum length"
Expand All @@ -50,6 +59,11 @@ export interface AdtContract {
functions: FunctionsContract;
ddic: DdicContract;
system: SystemContract;
rap: {
behavdeft: BehavdeftContract;
ddls: DdlsContract;
generator: GeneratorContract;
};
}

export const adtContract: AdtContract = {
Expand All @@ -65,6 +79,11 @@ export const adtContract: AdtContract = {
functions: functionsContract,
ddic: ddicContract,
system: systemContract,
rap: {
behavdeft: behavdeftContract,
ddls: ddlsContract,
generator: generatorContract,
},
};

// Import RestClient from base for client type definition
Expand Down
31 changes: 31 additions & 0 deletions packages/adt-contracts/src/adt/rap/behavdeft.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* ADT RAP Behavior Definition Contract
*
* Endpoint: /sap/bc/adt/rap/behavdeft
*
* Behavior Definitions (BDEF) define the behavior of RAP business objects.
* They specify which operations (create, update, delete, read, actions)
* are supported and how they behave.
*
* The BDEF contract supports:
* - GET: Retrieve BDEF metadata
* - POST: Create new BDEF
* - PUT: Update existing BDEF
* - DELETE: Remove BDEF
* - Lock/Unlock for editing
* - Source code access
*/

import { crud } from '../../base';
import { classes as classesSchema } from '../../schemas';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Wrong schema (classes) used for Behavior Definition contract — will misparse XML at runtime

The behavdeftContract uses classes (the ABAP OO Classes schema rooted in namespace http://www.sap.com/adt/oo/classes with AbapClass as root element) as its schema. This schema defines class-specific attributes like final, abstract, sharedMemoryEnabled, constructorGenerated, etc. (packages/adt-schemas/src/schemas/generated/schemas/sap/classes.ts). When the client calls adtContract.rap.behavdeft.get(...), the classes schema's parse() will attempt to interpret BDEF XML as OO class XML, producing incorrect/empty results. Similarly, post()/put() will build() OO class XML instead of BDEF XML. Every other contract in the codebase uses its matching schema (e.g., domainsdomain, interfacesinterfaces).

Prompt for agents
The behavdeft contract at packages/adt-contracts/src/adt/rap/behavdeft.ts:20 imports and uses the `classes` schema (import { classes as classesSchema } from '../../schemas') which is the ABAP OO Classes XSD schema. Behavior Definitions have a completely different XML structure. Either:
1. Create a proper XSD-derived schema for behavior definitions (preferred, following the ts-xsd pipeline: XSD → adt-schemas → adt-contracts/schemas.ts)
2. If no XSD is available yet, at minimum add a comment explaining this is a temporary placeholder and document the limitation

The same issue exists in ddls.ts and generator.ts which also incorrectly use the classes schema.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


export const behavdeftContract = crud({
basePath: '/sap/bc/adt/rap/behavdeft',
schema: classesSchema,
contentType: 'application/vnd.sap.adt.rap.behavdeft.v1+xml',
accept:
'application/vnd.sap.adt.rap.behavdeft.v1+xml, application/vnd.sap.adt.rap.behavdeft+xml',
sources: ['main'] as const,
});

export type BehavdeftContract = typeof behavdeftContract;
31 changes: 31 additions & 0 deletions packages/adt-contracts/src/adt/rap/ddls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* ADT RAP CDS View/Entity Contract
*
* Endpoint: /sap/bc/adt/ddl/ddls
*
* CDS Views and View Entities are core RAP data sources.
* DDLS (Data Definition Language Source) objects define:
* - CDS Views: define projection views on database tables
* - View Entities: RAP-managed CDS views with behavior
*
* This contract handles CRUD operations for all DDLS objects,
* including RAP-managed ones that have behavior definitions.
*
* Note: While DDLS is the generic DDL endpoint, RAP-managed
* CDS objects are specifically identified by their type
* (e.g., zrap_c_view for managed view entities).
*/

import { crud } from '../../base';
import { classes as classesSchema } from '../../schemas';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Wrong schema (classes) used for DDLS contract — will misparse CDS view XML at runtime

Same issue as with behavdeft: the ddlsContract uses the classes (OO Classes) schema for CDS View/Entity (DDLS) objects at /sap/bc/adt/ddl/ddls. DDLS endpoints return DDL source XML (application/vnd.sap.adt.ddl.ddlsource), not OO class XML. The classes schema's parse() will fail or produce incorrect data when processing DDLS responses.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


export const ddlsContract = crud({
basePath: '/sap/bc/adt/ddl/ddls',
schema: classesSchema,
contentType: 'application/vnd.sap.adt.ddl.ddlsource.v2+xml',
accept:
'application/vnd.sap.adt.ddl.ddlsource.v2+xml, application/vnd.sap.adt.ddl.ddlsource.v1+xml, application/vnd.sap.adt.ddl.ddlsource+xml',
sources: ['main'] as const,
});

export type DdlsContract = typeof ddlsContract;
156 changes: 156 additions & 0 deletions packages/adt-contracts/src/adt/rap/generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/**
* ADT RAP Generator Workspace Contract
*
* Endpoint: /sap/bc/adt/rap/generator
*
* RAP Generator is used to generate RAP artifacts from CDS views.
* It can create:
* - Business object (BO) projections
* - Behavior definitions
* - Service bindings
* - OData services
*
* The generator workspace manages the generation process
* and tracks which artifacts have been generated.
*/

import { http } from '../../base';
import { classes as classesSchema } from '../../schemas';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Wrong schema (classes) used for Generator contract — will misparse RAP generator XML at runtime

Same issue as with behavdeft and ddls: the generatorContract uses the classes (OO Classes) schema for the RAP Generator endpoint at /sap/bc/adt/rap/generator. The generator returns application/vnd.sap.adt.rap.generator XML, not OO class XML. The list(), get(), create(), and update() operations all reference classesSchema for body and response schemas, which will produce incorrect parsing/building.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


export const generatorContract = {
/**
* GET /sap/bc/adt/rap/generator - List generator workspace contents
*/
list: () =>
http.get('/sap/bc/adt/rap/generator', {
responses: { 200: classesSchema },
headers: {
Accept:
'application/vnd.sap.adt.rap.generator.v1+xml, application/vnd.sap.adt.rap.generator+xml',
},
}),

/**
* GET /sap/bc/adt/rap/generator/{name} - Get generator workspace item
*/
get: (name: string) =>
http.get(`/sap/bc/adt/rap/generator/${name.toLowerCase()}`, {
responses: { 200: classesSchema },
headers: {
Accept:
'application/vnd.sap.adt.rap.generator.v1+xml, application/vnd.sap.adt.rap.generator+xml',
},
}),

/**
* POST /sap/bc/adt/rap/generator - Create generator workspace item
*/
create: (options?: { corrNr?: string }) =>
http.post('/sap/bc/adt/rap/generator', {
body: classesSchema,
responses: { 200: classesSchema },
headers: {
Accept:
'application/vnd.sap.adt.rap.generator.v1+xml, application/vnd.sap.adt.rap.generator+xml',
'Content-Type': 'application/vnd.sap.adt.rap.generator.v1+xml',
},
query: options?.corrNr ? { corrNr: options.corrNr } : undefined,
}),

/**
* PUT /sap/bc/adt/rap/generator/{name} - Update generator workspace item
*/
update: (name: string, options?: { corrNr?: string; lockHandle?: string }) =>
http.put(`/sap/bc/adt/rap/generator/${name.toLowerCase()}`, {
body: classesSchema,
responses: { 200: classesSchema },
headers: {
Accept:
'application/vnd.sap.adt.rap.generator.v1+xml, application/vnd.sap.adt.rap.generator+xml',
'Content-Type': 'application/vnd.sap.adt.rap.generator.v1+xml',
},
query: {
...(options?.corrNr ? { corrNr: options.corrNr } : {}),
...(options?.lockHandle ? { lockHandle: options.lockHandle } : {}),
},
}),

/**
* DELETE /sap/bc/adt/rap/generator/{name} - Delete generator workspace item
*/
delete: (name: string, options?: { corrNr?: string; lockHandle?: string }) =>
http.delete(`/sap/bc/adt/rap/generator/${name.toLowerCase()}`, {
responses: { 204: undefined },
query: {
...(options?.corrNr ? { corrNr: options.corrNr } : {}),
...(options?.lockHandle ? { lockHandle: options.lockHandle } : {}),
},
}),

/**
* POST /sap/bc/adt/rap/generator/{name}?_action=LOCK - Lock for editing
*/
lock: (name: string, options?: { corrNr?: string }) =>
http.post(`/sap/bc/adt/rap/generator/${name.toLowerCase()}`, {
responses: { 200: undefined },
headers: {
'X-sap-adt-sessiontype': 'stateful',
'x-sap-security-session': 'use',
Accept:
'application/*,application/vnd.sap.as+xml;charset=UTF-8;dataname=com.sap.adt.lock.result',
},
query: {
_action: 'LOCK',
accessMode: 'MODIFY',
...(options?.corrNr ? { corrNr: options.corrNr } : {}),
},
}),

/**
* POST /sap/bc/adt/rap/generator/{name}?_action=UNLOCK - Unlock
*/
unlock: (name: string, lockHandle: string) =>
http.post(`/sap/bc/adt/rap/generator/${name.toLowerCase()}`, {
responses: { 200: undefined },
headers: {
'X-sap-adt-sessiontype': 'stateful',
'x-sap-security-session': 'use',
},
query: {
_action: 'UNLOCK',
accessMode: 'MODIFY',
lockHandle,
},
}),
Comment on lines +112 to +124
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Generator unlock API signature inconsistent with crud-based contracts in same namespace

The hand-written generatorContract.unlock takes (name: string, lockHandle: string) as a positional string parameter (packages/adt-contracts/src/adt/rap/generator.ts:112), while the crud()-generated contracts (behavdeft, ddls, and all other contracts) define unlock(name: string, options: UnlockOptions) where UnlockOptions = { lockHandle: string } (packages/adt-contracts/src/helpers/crud.ts:440). This means consumers calling adtContract.rap.behavdeft.unlock('x', { lockHandle: '...' }) must use a different pattern for adtContract.rap.generator.unlock('x', '...') within the same rap namespace, creating an inconsistent API surface.

Suggested change
unlock: (name: string, lockHandle: string) =>
http.post(`/sap/bc/adt/rap/generator/${name.toLowerCase()}`, {
responses: { 200: undefined },
headers: {
'X-sap-adt-sessiontype': 'stateful',
'x-sap-security-session': 'use',
},
query: {
_action: 'UNLOCK',
accessMode: 'MODIFY',
lockHandle,
},
}),
unlock: (name: string, options: { lockHandle: string }) =>
http.post(`/sap/bc/adt/rap/generator/${name.toLowerCase()}`, {
responses: { 200: undefined },
headers: {
'X-sap-adt-sessiontype': 'stateful',
'x-sap-security-session': 'use',
},
query: {
_action: 'UNLOCK',
accessMode: 'MODIFY',
lockHandle: options.lockHandle,
},
}),
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


/**
* GET /sap/bc/adt/rap/generator/{name}/source/main - Get source code
*/
getSource: (name: string) =>
http.get(`/sap/bc/adt/rap/generator/${name.toLowerCase()}/source/main`, {
responses: { 200: undefined as unknown as string },
headers: { Accept: 'text/plain' },
}),

/**
* PUT /sap/bc/adt/rap/generator/{name}/source/main - Update source code
*/
putSource: (
name: string,
options?: { corrNr?: string; lockHandle?: string },
) =>
http.put(`/sap/bc/adt/rap/generator/${name.toLowerCase()}/source/main`, {
body: undefined as unknown as string,
responses: { 200: undefined as unknown as string },
headers: {
Accept: 'text/plain',
'Content-Type': 'text/plain',
},
query: {
...(options?.corrNr ? { corrNr: options.corrNr } : {}),
...(options?.lockHandle ? { lockHandle: options.lockHandle } : {}),
},
}),
};

export type GeneratorContract = typeof generatorContract;
16 changes: 16 additions & 0 deletions packages/adt-contracts/src/adt/rap/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* ADT RAP (RESTful ABAP Programming) Contracts
*
* RAP is the modern ABAP development model for building OData services.
* These contracts cover:
* - Behavior Definitions (BDEF) - /sap/bc/adt/rap/behavdeft
* - CDS View/Entity (DDLS) - /sap/bc/adt/ddl/ddls
* - RAP Generator - /sap/bc/adt/rap/generator
*
* RAP-managed CDS objects extend standard DDIC objects with additional
* metadata and versioning for the RAP framework.
*/

export * from './behavdeft';
export * from './ddls';
export * from './generator';
Loading
Loading