-
Notifications
You must be signed in to change notification settings - Fork 1
feat(ddic): add full DDIC object type support (DOMA, DTEL, TABL, TTYP) #84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
41b6a4b
ac0d580
f16f525
355c838
2816dd4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| +18 −0 | src/ddic/zage_string_table.ttyp.xml | |
| +18 −0 | src/ddic/zage_struct_table_type.ttyp.xml | |
| +44 −0 | src/ddic/zage_structure.tabl.xml | |
| +46 −0 | src/ddic/zage_tabl.tabl.xml |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| /** | ||
| * DOMA - Domain | ||
| * | ||
| * ADK object for ABAP Domains (DOMA). | ||
| * DDIC objects are metadata-only (no source code). | ||
| */ | ||
|
|
||
| import { AdkMainObject } from '../../../base/model'; | ||
| import { Domain as DomainKind } from '../../../base/kinds'; | ||
| import { getGlobalContext } from '../../../base/global-context'; | ||
| import type { AdkContext } from '../../../base/context'; | ||
|
|
||
| import type { DomainResponse } from '../../../base/adt'; | ||
|
|
||
| /** | ||
| * Domain data type - unwrap from schema root element | ||
| */ | ||
| export type DomainXml = DomainResponse['domain']; | ||
|
|
||
| /** | ||
| * ADK Domain object | ||
| * | ||
| * Inherits from AdkMainObject which provides: | ||
| * - name, type, description, version, language, changedBy/At, createdBy/At, links | ||
| * - package, packageRef, responsible, masterLanguage, masterSystem, abapLanguageVersion | ||
| * | ||
| * Domain-specific properties via `data`: | ||
| * - data.typeInformation (dataType, length, decimals, outputLength) | ||
| * - data.outputInformation (conversionExit, signPresentation, lowerCase) | ||
| * - data.fixedValues | ||
| */ | ||
| export class AdkDomain extends AdkMainObject<typeof DomainKind, DomainXml> { | ||
| static readonly kind = DomainKind; | ||
| readonly kind = AdkDomain.kind; | ||
|
|
||
| get objectUri(): string { | ||
| return `/sap/bc/adt/ddic/domains/${encodeURIComponent(this.name.toLowerCase())}`; | ||
| } | ||
|
|
||
| protected override get wrapperKey() { | ||
| return 'domain'; | ||
| } | ||
| protected override get crudContract(): any { | ||
| return this.ctx.client.adt.ddic.domains; | ||
| } | ||
|
|
||
| static async get(name: string, ctx?: AdkContext): Promise<AdkDomain> { | ||
| const context = ctx ?? getGlobalContext(); | ||
| return new AdkDomain(context, name).load(); | ||
| } | ||
| } | ||
|
|
||
| // Self-register with ADK registry | ||
| import { registerObjectType } from '../../../base/registry'; | ||
| registerObjectType('DOMA', DomainKind, AdkDomain); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| /** | ||
| * DOMA - Domain | ||
| */ | ||
|
|
||
| export { AdkDomain } from './doma.model'; | ||
| export type { DomainXml } from './doma.model'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| /** | ||
| * DTEL - Data Element | ||
| * | ||
| * ADK object for ABAP Data Elements (DTEL). | ||
| * DDIC objects are metadata-only (no source code). | ||
| */ | ||
|
|
||
| import { AdkMainObject } from '../../../base/model'; | ||
| import { DataElement as DataElementKind } from '../../../base/kinds'; | ||
| import { getGlobalContext } from '../../../base/global-context'; | ||
| import type { AdkContext } from '../../../base/context'; | ||
|
|
||
| import type { DataElementResponse } from '../../../base/adt'; | ||
|
|
||
| /** | ||
| * Data Element data type - unwrap from wrapper root element | ||
| * SAP wraps DTEL content in a blue:wbobj element extending AdtMainObject | ||
| */ | ||
| export type DataElementXml = DataElementResponse['wbobj']; | ||
|
|
||
| /** | ||
| * ADK Data Element object | ||
| * | ||
| * Inherits from AdkMainObject which provides: | ||
| * - name, type, description, version, language, changedBy/At, createdBy/At, links | ||
| * - package, packageRef, responsible, masterLanguage, masterSystem, abapLanguageVersion | ||
| * | ||
| * Data element-specific properties via `data`: | ||
| * - data.typeKind, data.typeName, data.dataType | ||
| * - data.shortDescription, data.mediumDescription, data.longDescription, data.headingDescription | ||
| * - data.searchHelp | ||
| */ | ||
| export class AdkDataElement extends AdkMainObject< | ||
| typeof DataElementKind, | ||
| DataElementXml | ||
| > { | ||
| static readonly kind = DataElementKind; | ||
| readonly kind = AdkDataElement.kind; | ||
|
|
||
| get objectUri(): string { | ||
| return `/sap/bc/adt/ddic/dataelements/${encodeURIComponent(this.name.toLowerCase())}`; | ||
| } | ||
|
|
||
| protected override get wrapperKey() { | ||
| return 'wbobj'; | ||
| } | ||
| protected override get crudContract(): any { | ||
| return this.ctx.client.adt.ddic.dataelements; | ||
|
Comment on lines
+47
to
+48
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Verify explicit any usage in ADK DDIC models
rg -nP --type=ts 'crudContract\(\):\s*any' packages/adk/src/objects/ddicRepository: abapify/adt-cli Length of output: 553 🏁 Script executed: #!/bin/bash
# Inspect dtel.model.ts file to see imports and class structure
head -60 packages/adk/src/objects/ddic/dtel/dtel.model.ts | cat -nRepository: abapify/adt-cli Length of output: 2333 🏁 Script executed: #!/bin/bash
# Find the base class that defines crudContract
fd -e ts -p "packages/adk/src" | xargs rg -l "class.*Model.*\{" | head -5Repository: abapify/adt-cli Length of output: 41 🏁 Script executed: #!/bin/bash
# Search for the base class definition of DDIC models (likely in packages/adk/src/objects)
rg -A 5 "class DtelModel" packages/adk/src/objects/ddic/dtel/dtel.model.ts | head -20Repository: abapify/adt-cli Length of output: 41 🏁 Script executed: #!/bin/bash
# Check what type ctx.client.adt.ddic.dataelements actually is
# by looking at client definition and types
find packages/adt-client/src -name "*contract*" | xargs rg -l "dataelements" | head -3Repository: abapify/adt-cli Length of output: 203 🏁 Script executed: #!/bin/bash
# Check base class AdkMainObject definition
rg -A 20 "class AdkMainObject" packages/adk/src/base/model.ts | head -40Repository: abapify/adt-cli Length of output: 761 🏁 Script executed: #!/bin/bash
# Look for crudContract definition in base class
rg -B 2 -A 5 "crudContract" packages/adk/src/base/model.ts | head -30Repository: abapify/adt-cli Length of output: 901 🏁 Script executed: #!/bin/bash
# Check AdkContext type definition to understand ctx.client structure
rg -A 30 "export.*type AdkContext" packages/adk/src/base/context.ts | head -50Repository: abapify/adt-cli Length of output: 41 🏁 Script executed: #!/bin/bash
# Check if other non-DDIC models use proper types for crudContract
rg "crudContract.*:" packages/adk/src/objects --type=ts | grep -v "any" | head -10Repository: abapify/adt-cli Length of output: 41 🏁 Script executed: #!/bin/bash
# Find context.ts and check AdkContext definition more carefully
find packages/adk/src -name "context.ts" -exec cat {} \;Repository: abapify/adt-cli Length of output: 1069 🏁 Script executed: #!/bin/bash
# Check the get signature in crudContract to understand expected type
rg -B 5 -A 10 "get.*crudContract" packages/adk/src/base/model.ts | head -50Repository: abapify/adt-cli Length of output: 569 🏁 Script executed: #!/bin/bash
# Look at the contract type - check what dataelements contract looks like
head -50 packages/adt-contracts/src/adt/ddic/dataelements.tsRepository: abapify/adt-cli Length of output: 785 🏁 Script executed: #!/bin/bash
# Check if any ADK models override crudContract with a specific type (not any)
rg "override get crudContract" packages/adk/src/objects -A 1 | head -40Repository: abapify/adt-cli Length of output: 2059 🏁 Script executed: #!/bin/bash
# Check what types are exported from dataelements contract
cat -n packages/adt-contracts/src/adt/ddic/dataelements.tsRepository: abapify/adt-cli Length of output: 967 🏁 Script executed: #!/bin/bash
# Check if contracts are imported in ADK or if there's a typing gap
rg "import.*dataelements" packages/adk/src/objects/ddic/dtel/ | head -10Repository: abapify/adt-cli Length of output: 41 🏁 Script executed: #!/bin/bash
# Check the base class to see if the any has justification
cat -n packages/adk/src/base/model.ts | sed -n '200,250p'Repository: abapify/adt-cli Length of output: 1742 🏁 Script executed: #!/bin/bash
# Check if AdtClient defines the contract types properly
rg -A 20 "export.*AdtClient\|interface AdtClient" packages/adk/src/base/adt.ts | head -50Repository: abapify/adt-cli Length of output: 41 🏁 Script executed: #!/bin/bash
# Check if ADK models can import contract types from adt-contracts
rg "from.*adt-contracts" packages/adk/src/objects --type=ts | head -5Repository: abapify/adt-cli Length of output: 41 🏁 Script executed: #!/bin/bash
# Check how the contracts are organized - see if DataelementsContract is the right type
rg "export.*Contract" packages/adt-contracts/src/adt/ddic/ --type=tsRepository: abapify/adt-cli Length of output: 1170 🏁 Script executed: #!/bin/bash
# Check the actual adt.ts or client builder to understand if contracts are properly typed
fd -e ts "adt\\.ts|client\\.ts" packages/adk/src/base/ | xargs cat | head -100Repository: abapify/adt-cli Length of output: 3069 🏁 Script executed: #!/bin/bash
# Check if there's already a pattern for importing contract types in the codebase
rg "DataelementsContract\|programsContract\|classesContract" packages/adk/src --type=ts | head -5Repository: abapify/adt-cli Length of output: 41 🏁 Script executed: #!/bin/bash
# Check if `@abapify/adt-client` exports the contract types
rg "export.*Contract" packages/adt-contracts/src/index.ts | head -20Repository: abapify/adt-cli Length of output: 41 🏁 Script executed: #!/bin/bash
# Check the main adt-contracts/index.ts to see what's exported
cat packages/adt-contracts/src/index.ts | head -100Repository: abapify/adt-cli Length of output: 932 🏁 Script executed: #!/bin/bash
# Verify if ADK can import from adt-contracts or adt-client
grep -r "from.*@abapify" packages/adk/src --include="*.ts" | head -5Repository: abapify/adt-cli Length of output: 401 🏁 Script executed: #!/bin/bash
# Check if the suggested type signature actually works by testing the path
cat -n packages/adk/src/base/context.tsRepository: abapify/adt-cli Length of output: 1335 🏁 Script executed: #!/bin/bash
# Check if AdtClient type definition properly types the contract nested access
rg -A 30 "interface AdtClient\|type AdtClient" packages/adt-client/src --type=ts | head -60Repository: abapify/adt-cli Length of output: 41 🏁 Script executed: #!/bin/bash
# Check how AdtClient.adt.ddic.dataelements is typed - is it strongly typed?
rg "ddic.*dataelements\|dataelements.*:" packages/adt-client/src --type=ts -B 2 -A 2 | head -30Repository: abapify/adt-cli Length of output: 41 🏁 Script executed: #!/bin/bash
# Check if the base class comment explains why any is used
rg -B 10 "protected get crudContract.*any" packages/adk/src/base/model.ts | head -20Repository: abapify/adt-cli Length of output: 370 🏁 Script executed: #!/bin/bash
# Check the actual AdtClient type structure to understand if the nested property type is accessible
find packages/adt-client/src -name "*.ts" | xargs rg "ddic.*dataelements\|export.*adt:" | head -20Repository: abapify/adt-cli Length of output: 41 🏁 Script executed: #!/bin/bash
# Check if adt-contracts properly exports DataelementsContract for import
cat packages/adt-contracts/src/adt/ddic/index.tsRepository: abapify/adt-cli Length of output: 1361 🏁 Script executed: #!/bin/bash
# Verify if any ADK model imports contract types from adt-contracts
rg "import.*Contract.*from" packages/adk/src --type=tsRepository: abapify/adt-cli Length of output: 41 🏁 Script executed: #!/bin/bash
# Verify that AdtClient typing doesn't provide inferred contract types for nested access
# Check if adt-client exports AdtClient with proper nested typing
cat packages/adt-client/src/index.tsRepository: abapify/adt-cli Length of output: 2615 🏁 Script executed: #!/bin/bash
# Check what package.json says about the adt-client version/exports
cat packages/adt-client/package.json | jq '.exports, .main'Repository: abapify/adt-cli Length of output: 131 Add TypeScript strict-mode justification or type All ADK object models override Either add the disable comment with justification: // eslint-disable-next-line `@typescript-eslint/no-explicit-any`
protected override get crudContract(): any {
return this.ctx.client.adt.ddic.dataelements;
}Or import and use the contract type directly: import type { DataelementsContract } from '@abapify/adt-contracts';
protected override get crudContract(): DataelementsContract {
return this.ctx.client.adt.ddic.dataelements;
}This pattern affects 10+ models across the codebase (dtel, doma, tabl, ttyp, clas, intf, prog, devc, fugr, and others). 🤖 Prompt for AI Agents
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dismissed — pre-existing base class limitation. ALL ADK models (CLAS, INTF, PROG, FUGR, DEVC + DDIC) use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
( ˘ω˘ ) 🐇 🧠 Learnings used |
||
| } | ||
|
|
||
| static async get(name: string, ctx?: AdkContext): Promise<AdkDataElement> { | ||
| const context = ctx ?? getGlobalContext(); | ||
| return new AdkDataElement(context, name).load(); | ||
| } | ||
| } | ||
|
|
||
| // Self-register with ADK registry | ||
| import { registerObjectType } from '../../../base/registry'; | ||
| registerObjectType('DTEL', DataElementKind, AdkDataElement); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| /** | ||
| * DTEL - Data Element | ||
| */ | ||
|
|
||
| export { AdkDataElement } from './dtel.model'; | ||
| export type { DataElementXml } from './dtel.model'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| /** | ||
| * DDIC - Data Dictionary Objects | ||
| */ | ||
|
|
||
| export { AdkDomain, type DomainXml } from './doma'; | ||
| export { AdkDataElement, type DataElementXml } from './dtel'; | ||
| export { AdkTable, AdkStructure, type TableXml } from './tabl'; | ||
| export { AdkTableType, type TableTypeXml } from './ttyp'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| /** | ||
| * TABL - Database Table / Structure | ||
| */ | ||
|
|
||
| export { AdkTable, AdkStructure } from './tabl.model'; | ||
| export type { TableXml } from './tabl.model'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't treat
lock()405s and createalready existsas a successful upsert.A
405fromlock()only proves the lock endpoint is unusable, not that the object is missing. If an existing DDIC object hits that path, we jump to POST and then collapse422 already existsinto_unchanged = true, so the caller gets a false success even though this instance's payload was never written. Please only fall back to create on a definitive not-found, and retry update or load+compare before treatingalready existsas unchanged.🔧 Minimal safe direction
} catch (e) { - if (mode === 'upsert' && this.shouldFallbackToCreate(e)) { + if (mode === 'upsert' && this.isNotFoundError(e)) { return this.fallbackToCreate(options); } throw e; } ... } catch (e: unknown) { if (mode === 'upsert' && this.shouldFallbackToCreate(e)) { return this.fallbackToCreate(options); } - // If PUT returns 422 "already exists" directly in upsert mode - if (mode === 'upsert' && this.isAlreadyExistsError(e)) { - this._unchanged = true; - return this; - } throw e; } ... } catch (createErr) { if (this.isAlreadyExistsError(createErr)) { - this._unchanged = true; - return this; + throw createErr; } throw createErr; }Also applies to: 517-524, 602-608
🤖 Prompt for AI Agents