Skip to content
Merged
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
5 changes: 3 additions & 2 deletions packages/adk/src/base/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,8 @@ export function resolveKind(kind: AdkKind): RegistryEntry | undefined {
* Get ADK kind for ADT type
*/
export function getKindForType(adtType: string): AdkKind | undefined {
return adtToKind.get(getMainType(adtType));
const normalizedType = adtType.toUpperCase();
return adtToKind.get(normalizedType) ?? adtToKind.get(getMainType(adtType));
Comment thread
ThePlenkov marked this conversation as resolved.
Comment thread
ThePlenkov marked this conversation as resolved.
Comment thread
ThePlenkov marked this conversation as resolved.
}

/**
Expand All @@ -201,7 +202,7 @@ export function getTypeForKind(kind: AdkKind): string | undefined {
* Check if ADT type is registered
*/
export function isTypeRegistered(adtType: string): boolean {
return registry.has(getMainType(adtType));
return resolveType(adtType) !== undefined;
}

/**
Expand Down
12 changes: 11 additions & 1 deletion packages/adk/src/objects/cts/transport-import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import type { AdkContext } from '../../base/context';
import { getGlobalContext } from '../../base/global-context';
import { getRegisteredTypes } from '../../base/registry';
import type { TransportGetResponse } from '../../base/adt';
import type { TransportData } from './transport/transport.types';

Expand Down Expand Up @@ -188,10 +189,19 @@ export class AdkTransportObjectRef {
const { createAdk } = await import('../../factory');
const adk = createAdk(this.ctx.client);

const ctsType = this.type.trim().toUpperCase();
const workbenchType = this.wbtype?.trim().toUpperCase();
const factoryType =
workbenchType &&
workbenchType.startsWith(`${ctsType}/`) &&
getRegisteredTypes().includes(workbenchType)
? workbenchType
: ctsType;
Comment thread
ThePlenkov marked this conversation as resolved.

// Use the factory to get the object
// The factory handles type resolution and loading
try {
const obj = adk.get(this.name, this.type);
const obj = adk.get(this.name, factoryType);
// Check if object has load method (AdkGenericObject doesn't)
if ('load' in obj && typeof obj.load === 'function') {
await obj.load();
Expand Down
3 changes: 3 additions & 0 deletions packages/adk/src/objects/repository/incl/incl.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,6 @@ import { registerObjectType } from '../../../base/registry';
registerObjectType('INCL', IncludeKind, AdkInclude, {
endpoint: 'programs/includes',
});
registerObjectType('PROG/I', IncludeKind, AdkInclude, {
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
endpoint: 'programs/includes',
});
Comment thread
ThePlenkov marked this conversation as resolved.
18 changes: 18 additions & 0 deletions packages/adk/tests/incl.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
import { describe, it, expect, vi } from 'vitest';
import { AdkInclude } from '../src/objects/repository/incl/incl.model';
import type { AdkContext } from '../src/base/context';
import { Include } from '../src/base/kinds';
import { getTypeForKind } from '../src/base/registry';
import { createAdkFactory } from '../src/factory';

// ── helpers ──────────────────────────────────────────────────────────────────

Expand Down Expand Up @@ -90,6 +93,21 @@ function createCtx(
// ── AdkInclude ──────────────────────────────────────────────────────────────

describe('AdkInclude', () => {
it('resolves PROG/I and legacy INCL to the include model and source endpoint', async () => {
const { ctx, includes } = createCtx();
const factory = createAdkFactory(ctx);
const typed = factory.get('ZAPI_INCLUDE', 'PROG/I');
const legacy = factory.get('ZAPI_LEGACY_INCLUDE', 'INCL');

expect(typed).toBeInstanceOf(AdkInclude);
expect(legacy).toBeInstanceOf(AdkInclude);
expect(getTypeForKind(Include)).toBe('PROG/I');

await (typed as AdkInclude).getSource();

expect(includes.source.main.get).toHaveBeenCalledWith('ZAPI_INCLUDE');
});

it('objectUri constructs lowercase URI for the include', () => {
const { ctx } = createCtx();
const obj = new AdkInclude(ctx, 'ZTEST_INCLUDE');
Expand Down
15 changes: 15 additions & 0 deletions packages/adk/tests/registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,14 @@ describe('getKindForType', () => {
expect(getKindForType('TABL/DS')).toBe(kinds.Table);
});

it('should prefer an exact registered subtype and fall back to its base type', () => {
registerObjectType('PROG', kinds.Program, mockCtor);
registerObjectType('PROG/I', kinds.Include, mockCtor);

expect(getKindForType('PROG/I')).toBe(kinds.Include);
expect(getKindForType('PROG/UNKNOWN')).toBe(kinds.Program);
});

it('should return undefined for unregistered type', () => {
expect(getKindForType('UNREG')).toBeUndefined();
});
Expand Down Expand Up @@ -191,6 +199,13 @@ describe('isTypeRegistered', () => {
registerObjectType('TABL', kinds.Table, mockCtor);
expect(isTypeRegistered('TABL/DS')).toBe(true);
});

it('should recognize an exact subtype without treating unknown siblings as registered', () => {
registerObjectType('TABL/DS', kinds.Structure as AdkKind, mockCtor);

expect(isTypeRegistered('TABL/DS')).toBe(true);
expect(isTypeRegistered('TABL/UNKNOWN')).toBe(false);
});
});

describe('getRegisteredTypes', () => {
Expand Down
47 changes: 45 additions & 2 deletions packages/adk/tests/transport-import.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@

import { describe, it, expect, beforeEach, vi } from 'vitest';

const factoryGet = vi.fn();

vi.mock('../src/factory', () => ({
createAdk: vi.fn(() => ({ get: factoryGet })),
}));

// Mock the ADT client
// Response must have 'root' wrapper to match TransportmanagementSingleSchema
const mockTransportResponse = {
Expand Down Expand Up @@ -694,6 +700,11 @@ describe('MergedTransportView', () => {
});

describe('AdkTransportObjectRef', () => {
beforeEach(() => {
factoryGet.mockReset();
factoryGet.mockReturnValue({ load: vi.fn(async () => undefined) });
});

it('should have correct type and name', async () => {
const mockClient = createMockClient();

Expand Down Expand Up @@ -723,8 +734,40 @@ describe('AdkTransportObjectRef', () => {
expect(objRef.isDeleted).toBe(false);
});

// Note: objRef.load() test would require mocking the ADK factory
// which is more complex - skipping for now as it requires full ADK setup
it('uses a registered workbench subtype to select the factory object', async () => {
const { AdkTransportObjectRef } = await import('../src/index');
const objRef = new AdkTransportObjectRef({} as any, {
pgmid: 'R3TR',
type: 'TABL',
wbtype: 'TABL/DS',
name: 'ZAPI_STRUCTURE',
});

await objRef.load();

expect(factoryGet).toHaveBeenCalledWith('ZAPI_STRUCTURE', 'TABL/DS');
});

it('falls back to the normalized CTS type when the workbench subtype is absent or unregistered', async () => {
const { AdkTransportObjectRef } = await import('../src/index');
const absentSubtype = new AdkTransportObjectRef({} as any, {
pgmid: 'R3TR',
type: ' tabl ',
name: 'ZAPI_TABLE',
});
const unregisteredSubtype = new AdkTransportObjectRef({} as any, {
pgmid: 'R3TR',
type: ' tabl ',
wbtype: ' tabl/unknown ',
name: 'ZAPI_UNKNOWN',
});

await absentSubtype.load();
await unregisteredSubtype.load();

expect(factoryGet).toHaveBeenNthCalledWith(1, 'ZAPI_TABLE', 'TABL');
expect(factoryGet).toHaveBeenNthCalledWith(2, 'ZAPI_UNKNOWN', 'TABL');
});
});

describe('AdkTransportTaskRef', () => {
Expand Down
1 change: 1 addition & 0 deletions packages/adt-plugin-abapgit/src/lib/handlers/adk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export {
AdkInterface,
AdkPackage,
AdkProgram,
AdkInclude,
AdkFunctionGroup,
AdkFunctionModule,
AdkDomain,
Expand Down
37 changes: 31 additions & 6 deletions packages/adt-plugin-abapgit/src/lib/handlers/objects/prog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,27 @@
* Program (PROG) object handler for abapGit format
*/

import { AdkProgram } from '../adk';
import { AdkInclude, AdkProgram } from '../adk';
import { prog } from '../../../schemas/generated';
import { createHandler } from '../base';

export const programHandler = createHandler(AdkProgram, {
function programSubc(obj: AdkProgram | AdkInclude): string {
if (obj.kind === AdkInclude.kind) return 'I';
if (!obj.isLoaded) return '1';
switch (obj.dataSync.programType) {
Comment thread
ThePlenkov marked this conversation as resolved.
case 'modulePool':
return 'M';
case 'subroutinePool':
return 'S';
default:
return '1';
}
Comment thread
ThePlenkov marked this conversation as resolved.
}

export const programHandler = createHandler<
AdkProgram | AdkInclude,
typeof prog
>('PROG', {
schema: prog,
version: 'v1.0.0',
serializer: 'LCL_OBJECT_PROG',
Expand All @@ -17,7 +33,7 @@
PROGDIR: {
NAME: obj.name ?? '',
STATE: 'A',
SUBC: '1',
SUBC: programSubc(obj),
FIXPT: 'X',
UNICODE: 'X',
},
Expand All @@ -38,13 +54,22 @@
// SUBC can be parsed as number or string depending on XML parser
const subc = String(PROGDIR?.SUBC ?? '');

return {
const data = {
name: (PROGDIR?.NAME ?? '').toUpperCase(),
type: 'PROG/P',
description: descriptionEntry?.ENTRY ?? 'Program',
fixPointArithmetic: PROGDIR?.FIXPT === 'X',
activeUnicodeCheck: PROGDIR?.UNICODE === 'X',
programType: subc === '1' ? 'executableProgram' : 'subroutinePool',
};
if (subc === 'I') return { ...data, type: 'PROG/I' };
return {
...data,
type: 'PROG/P',
programType:
subc === 'M'
? 'modulePool'
: subc === 'S'
? 'subroutinePool'
: 'executableProgram',

Check warning on line 72 in packages/adt-plugin-abapgit/src/lib/handlers/objects/prog.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=abapify_adt-cli&issues=AaFkoNIrJIbWnGClYZGc&open=AaFkoNIrJIbWnGClYZGc&pullRequest=237
};
},

Expand Down
Loading
Loading