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
4 changes: 4 additions & 0 deletions cds-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ cds.build?.register?.('process-validation', ProcessValidationPlugin);
// Register import handler for: cds import --from process
// @ts-expect-error: import does not exist on cds type
cds.import ??= {};
//@ts-expect-error: cds type does not exist
cds.import.options ??= {};
//@ts-expect-error: cds type does not exist
cds.import.options.process = { no_copy: true, as: 'cds', config: 'kind=rest' };
// @ts-expect-error: process does not exist on cds.import type
cds.import.from ??= {};
// @ts-expect-error: from does not exist on cds.import type
Expand Down
56 changes: 0 additions & 56 deletions lib/processImport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,6 @@ async function fetchAndSaveProcessDefinition(processName: string): Promise<Fetch
await fs.promises.mkdir(path.dirname(outputPath), { recursive: true });
await fs.promises.writeFile(outputPath, JSON.stringify(processHeader, null, 2), 'utf8');

const serviceName = `${projectId}.${capitalize(processHeader.identifier)}Service`;
await addServiceToPackageJson(serviceName, `srv/external/${processName}`);

return { filePath: outputPath, processHeader };
}

Expand Down Expand Up @@ -111,40 +108,9 @@ async function generateCsnModel(jsonFilePath: string): Promise<csn.CsnModel> {
const processHeader = loadProcessHeader(jsonFilePath);
const csnModel = buildCsnModel(processHeader);

// Register service in package.json for local imports too
const serviceName = `${processHeader.projectId}.${capitalize(processHeader.identifier)}Service`;
const modelPath = getModelPathFromFilePath(jsonFilePath);
await addServiceToPackageJson(serviceName, modelPath);

return csnModel;
}

/**
* Convert absolute/relative file path to model path for package.json
* e.g., "./srv/external/foo.json" -> "srv/external/foo"
* "/abs/path/srv/external/foo.json" -> "srv/external/foo"
*/
function getModelPathFromFilePath(filePath: string): string {
// Resolve to absolute, then make relative to cds.root
const absolutePath = path.resolve(filePath);
let relativePath = path.relative(cds.root, absolutePath);

// Remove .json extension
if (relativePath.endsWith('.json')) {
relativePath = relativePath.slice(0, -5);
}

// Normalize path separators
relativePath = relativePath.replace(/\\/g, '/');

// Replace "workflows" prefix with "srv/external"
if (relativePath.startsWith('workflows/')) {
relativePath = 'srv/external/' + relativePath.slice('workflows/'.length);
}

return relativePath;
}

function loadProcessHeader(filePath: string): ProcessHeader {
const content = fs.readFileSync(path.resolve(filePath), 'utf-8');
const header = JSON.parse(content) as ProcessHeader;
Expand Down Expand Up @@ -562,28 +528,6 @@ function ensureObjectSchema(schema?: JsonSchema): JsonSchema {
return { type: 'object', properties: {}, required: [] };
}

// ============================================================================
// PACKAGE.JSON UPDATE
// ============================================================================

async function addServiceToPackageJson(serviceName: string, modelPath: string): Promise<void> {
const packagePath = path.join(cds.root, 'package.json');

try {
const content = await fs.promises.readFile(packagePath, 'utf8');
const pkg = JSON.parse(content);

pkg.cds ??= {};
pkg.cds.requires ??= {};
pkg.cds.requires[serviceName] = { kind: 'external', model: modelPath };

await fs.promises.writeFile(packagePath, JSON.stringify(pkg, null, 2) + '\n', 'utf8');
LOG.debug(`Added ${serviceName} to package.json`);
} catch (error) {
LOG.warn(`Could not update package.json: ${error}`);
}
}

// ============================================================================
// UTILITIES
// ============================================================================
Expand Down
2 changes: 1 addition & 1 deletion tests/bookshop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
}
},
"eu12.bpm-horizon-walkme.sdshipmentprocessor.ShipmentHandlerService": {
"kind": "external",
"kind": "rest",
"model": "srv/external/eu12.bpm-horizon-walkme.sdshipmentprocessor.shipmentHandler"
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* checksum : ba13b7a95c8d1c3567a2bdb109bd687d */
/* checksum : 060851d153c6463bb8c4fb247ccfd927 */
namespace eu12.![bpm-horizon-walkme].sdshipmentprocessor;

/** DO NOT EDIT. THIS IS A GENERATED SERVICE THAT WILL BE OVERRIDDEN ON NEXT IMPORT. */
Expand Down Expand Up @@ -39,7 +39,7 @@ service ShipmentHandlerService {
};

type ProcessOutputs {
shipmentProcessResultOutput : ShipmentProcessResult not null;
shipmentProcessResultOutput : ShipmentProcessResult;
};

type ProcessAttribute {
Expand All @@ -62,6 +62,8 @@ service ShipmentHandlerService {

type ProcessInstances : many ProcessInstance;

type ProcessInstanceStatus : many String;

action start(
inputs : ProcessInputs not null
);
Expand All @@ -76,7 +78,7 @@ service ShipmentHandlerService {

function getInstancesByBusinessKey(
businessKey : String not null,
status : many String
status : ProcessInstanceStatus
) returns ProcessInstances;

action suspend(
Expand Down
86 changes: 0 additions & 86 deletions tests/integration/process-import/process-import.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,92 +363,6 @@ describe('Process Import Integration Tests', () => {
});
});

describe('Package.json Update', () => {
it('should add service to package.json with kind external', async () => {
const targetPath = path.join(tempDir, 'srv', 'external', 'test.project.simpleProcess.json');
await fs.promises.copyFile(simpleProcessPath, targetPath);

await importProcess(targetPath);

const packageJsonPath = path.join(tempDir, 'package.json');
const packageJson = JSON.parse(await fs.promises.readFile(packageJsonPath, 'utf8'));

expect(packageJson.cds).toBeDefined();
expect(packageJson.cds.requires).toBeDefined();
expect(packageJson.cds.requires['test.project.SimpleProcessService']).toBeDefined();
expect(packageJson.cds.requires['test.project.SimpleProcessService'].kind).toBe('external');
expect(packageJson.cds.requires['test.project.SimpleProcessService'].model).toBe(
'srv/external/test.project.simpleProcess',
);
});

it('should compute correct model path from absolute file path', async () => {
const targetPath = path.join(tempDir, 'srv', 'external', 'my.custom.path.json');
await fs.promises.copyFile(simpleProcessPath, targetPath);

await importProcess(targetPath);

const packageJsonPath = path.join(tempDir, 'package.json');
const packageJson = JSON.parse(await fs.promises.readFile(packageJsonPath, 'utf8'));

expect(packageJson.cds.requires['test.project.SimpleProcessService'].model).toBe(
'srv/external/my.custom.path',
);
});

it('should preserve existing package.json content', async () => {
const existingPackageJson = {
name: 'test-project',
version: '1.0.0',
dependencies: { '@sap/cds': '^7.0.0' },
cds: {
requires: {
ExistingService: { kind: 'external', model: 'srv/existing' },
},
},
};
await fs.promises.writeFile(
path.join(tempDir, 'package.json'),
JSON.stringify(existingPackageJson, null, 2),
);

const targetPath = path.join(tempDir, 'srv', 'external', 'test.json');
await fs.promises.copyFile(simpleProcessPath, targetPath);

await importProcess(targetPath);

const packageJson = JSON.parse(
await fs.promises.readFile(path.join(tempDir, 'package.json'), 'utf8'),
);

expect(packageJson.name).toBe('test-project');
expect(packageJson.dependencies['@sap/cds']).toBe('^7.0.0');
expect(packageJson.cds.requires['ExistingService']).toBeDefined();
expect(packageJson.cds.requires['test.project.SimpleProcessService']).toBeDefined();
});

it('should create cds.requires if not present', async () => {
const packageJson = { name: 'test-project', version: '1.0.0' };
await fs.promises.writeFile(
path.join(tempDir, 'package.json'),
JSON.stringify(packageJson, null, 2),
);

const targetPath = path.join(tempDir, 'srv', 'external', 'test.json');
await fs.promises.copyFile(simpleProcessPath, targetPath);

await importProcess(targetPath);

const updatedPackageJson = JSON.parse(
await fs.promises.readFile(path.join(tempDir, 'package.json'), 'utf8'),
);

expect(updatedPackageJson.cds).toBeDefined();
expect(updatedPackageJson.cds.requires).toBeDefined();
expect(updatedPackageJson.cds.requires['test.project.SimpleProcessService']).toBeDefined();
});
});

describe('Edge Cases', () => {
it('should handle process with empty inputs', async () => {
const emptyInputsProcess = {
Expand Down
Loading