Skip to content

Commit

Permalink
fix: compile error when new .cds files are added to memfs
Browse files Browse the repository at this point in the history
  • Loading branch information
voicis committed Feb 27, 2025
1 parent 7a9757b commit d65559e
Show file tree
Hide file tree
Showing 13 changed files with 322 additions and 18 deletions.
5 changes: 5 additions & 0 deletions .changeset/serious-suits-beam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sap-ux/fiori-annotation-api': patch
---

Fixed CDS compile error when new .cds files are created in memfs.
17 changes: 17 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,23 @@
},
"cwd": "${workspaceFolder}/packages/cds-annotation-parser"
},
{
"type": "node",
"request": "launch",
"name": "cds-odata-annotation-converter: Debug Current Jest File",
"program": "${workspaceFolder}/node_modules/jest/bin/jest.js",
"args": ["${fileBasenameNoExtension}", "--config", "jest.config.js", "--coverage=false", "--runInBand"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"disableOptimisticBPs": true,
"windows": {
"program": "${workspaceFolder}/node_modules/jest/bin/jest"
},
"cwd": "${workspaceFolder}/packages/cds-odata-annotation-converter",
"env": {
"UX_DEBUG": "false"
}
},
{
"type": "node",
"request": "launch",
Expand Down
1 change: 1 addition & 0 deletions packages/fiori-annotation-api/jest.setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const fiveMinutes = 5 * 60000;
const TEST_DATA_ROOT = join(__dirname, 'test', 'data');
const CDS_PROJECTS = [
join(TEST_DATA_ROOT, 'cds', 'cap-start'),
join(TEST_DATA_ROOT, 'cds', 'cap-no-apps'),
join(TEST_DATA_ROOT, 'cds', 'layering'),
join(TEST_DATA_ROOT, 'cds', 'term-deletion')
];
Expand Down
2 changes: 1 addition & 1 deletion packages/fiori-annotation-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"@sap-ux/project-access": "workspace:*",
"@sap-ux/vocabularies-types": "0.13.0",
"@sap-ux/xml-odata-annotation-converter": "workspace:*",
"@sap/ux-cds-compiler-facade": "1.16.0",
"@sap/ux-cds-compiler-facade": "1.16.1",
"@xml-tools/ast": "5.0.5",
"@xml-tools/parser": "1.0.11",
"mem-fs": "2.1.0",
Expand Down
9 changes: 7 additions & 2 deletions packages/fiori-annotation-api/src/cds/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@ import type { CDSService, TextFile } from '../types';
* @param clearCache - Flag indicating if the CDS file resolution cache should be cleared.
* @returns CDS service structure.
*/
export async function getCDSService(projectRoot: string, serviceName: string, clearCache = false): Promise<CDSService> {
const files = await getCdsFiles(projectRoot, clearCache);
export async function getCDSService(
projectRoot: string,
serviceName: string,
fileCache: Map<string, string>,
clearCache = false
): Promise<CDSService> {
const files = await getCdsFiles(projectRoot, fileCache, clearCache);
const serviceFiles = files.map((uri): TextFile => {
return { uri: pathToFileURL(uri).toString(), isReadOnly: uri.indexOf('node_modules') !== -1 };
});
Expand Down
18 changes: 10 additions & 8 deletions packages/fiori-annotation-api/src/fiori-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ export class FioriAnnotationService {
project.projectType === 'CAPJava' || project.projectType === 'CAPNodejs'
);
const finalOptions = getOptionsWithDefaults(options);
const service = await getService(project, serviceName, appName, finalOptions.clearFileResolutionCache);
const service = await getService(project, serviceName, appName, fs, finalOptions.clearFileResolutionCache);
const adapter = createAdapter(
project,
service,
Expand Down Expand Up @@ -241,12 +241,6 @@ export class FioriAnnotationService {
this.fileCache.set(file.uri, file.content);
}

// all the modified files should also be included in the cache
for (const [relativePath, value] of Object.entries(this.fs.dump())) {
const absolute = pathToFileURL(join(process.cwd(), relativePath)).toString();
this.fileCache.set(absolute, value.contents);
}

await this.adapter.sync(this.fileCache);
this.isInitialSyncCompleted = true;
}
Expand Down Expand Up @@ -470,12 +464,20 @@ async function getService(
project: Project,
serviceName: string,
appName: string,
fsEditor: Editor | undefined,
clearCache: boolean
): Promise<Service> {
if (project.projectType === 'EDMXBackend') {
return getLocalEDMXService(project, serviceName, appName);
} else if (project.projectType === 'CAPJava' || project.projectType === 'CAPNodejs') {
return getCDSService(project.root, serviceName, clearCache);
const fileCache = new Map<string, string>();
if (fsEditor) {
for (const [relativePath, value] of Object.entries(fsEditor.dump())) {
const absolute = pathToFileURL(join(process.cwd(), relativePath)).toString();
fileCache.set(absolute, value.contents);
}
}
return getCDSService(project.root, serviceName, fileCache, clearCache);
} else {
throw new Error(`Unsupported project type "${project.projectType}"!`);
}
Expand Down
100 changes: 100 additions & 0 deletions packages/fiori-annotation-api/test/data/cds/cap-no-apps/db/schema.cds
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
namespace scp.cloud;

using {
managed,
cuid,
sap.common
} from '@sap/cds/common';

type Url : String;

type TechnicalBooleanFlag : Boolean @(
UI.Hidden,
Core.Computed
);
type Criticality : Integer @(
UI.Hidden,
Core.Computed
);

type Identifier : String(100)@(title : 'Identifier');
@cds.autoexpose
abstract entity identified : cuid {
identifier : Identifier not null;
}

//Bolded display of first table column values can be achieved by defining annotations Common.SemanticKey and
//Common.TextArrangement for the entities key and referring to a 'human-readable' identifier to be displayed instead.

annotate identified with @(
Common.SemanticKey : [identifier],
UI.Identification : [{Value : identifier}]
) {

ID @Common : {
Text : identifier,
TextArrangement : #TextOnly

};
}
entity Incidents : managed, identified {
title : String(50) @title : '{i18n>Title}';
category : Association to one Category @title : '{i18n>Category}';
priority : Association to one Priority @title : '{i18n>Priority}';
incidentStatus : Association to one IncidentStatus @title : '{i18n>IncidentStatus}';
description : String(1000) @title : '{i18n>IncidentDescription}';
assignedIndividual : Association to one Individual;
incidentFlow : Association to many IncidentFlow
on incidentFlow.incident = $self;
incidentProcessTimeline : Association to many IncidentProcessTimeline
on incidentProcessTimeline.incident = $self;
processingThreshold : Association to one ProcessingThreshold
on processingThreshold.incident = $self;
}

entity ProcessingThreshold {
key id : String(10);
processingDays : Integer;
processingLimit : Integer;
incident : Association to one Incidents;
}
entity IncidentFlow : managed {
key id : UUID;
processStep : String(30)@title : '{i18n>ProcessStep}';
stepStatus : String(10)@title : '{i18n>ProcessStepStatus}';
criticality : Integer;
stepStartDate : Date @title : '{i18n>StepStartDate}';
stepEndDate : Date @title : '{i18n>StepEndDate}';
incident : Association to Incidents;
}

entity IncidentProcessTimeline : managed {
key id : UUID;
text : String;
type : String;
startTime : DateTime;
endTime : DateTime;
incident : Association to Incidents;
}

entity Individual : managed {
key id : UUID;
//Begin add additional properties
businessPartnerID : String;
addressID : String;
//End add additional properties
Incidents : Association to many Incidents
on Incidents.assignedIndividual = $self;
}

entity IncidentsCodeList : common.CodeList {
key code : String(20);
}

entity Category : IncidentsCodeList {}

entity Priority : IncidentsCodeList {
criticality : Criticality not null default 3;
}

entity IncidentStatus : IncidentsCodeList {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "teched2020-iis360",
"version": "1.0.0",
"description": "A simple CAP project.",
"repository": "<Add your repository here>",
"license": "UNLICENSED",
"private": true,
"dependencies": {
"@sap/cds": "6.7.0"
},
"devDependencies": {},
"cds": {
"requires": {
"db": {
"kind": "sql"
}
},
"odata": {
"version": "v4"
}
},
"scripts": {
"start": "cds run"
},
"sapux": []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
namespace scp.cloud;
using IncidentService as service from './incidentservice';

using {
cuid
} from '@sap/cds/common';

annotate cuid with {
ID @(
title : '{i18n>ID}',
UI.HiddenFilter,
Core.Computed
);
}


annotate service.Incidents with {
ID @UI.Hidden: true;
assignedIndividual @UI.Hidden : true;
};

annotate service.Incidents with {
incidentStatus @Common : {
Text : incidentStatus.code,
TextArrangement : #TextOnly,
ValueListWithFixedValues
};
category @Common : {
Text : category.code,
TextArrangement : #TextOnly,
ValueListWithFixedValues
};
priority @Common : {
Text : priority.code,
TextArrangement : #TextOnly,
ValueListWithFixedValues
};
};

annotate service.Category with {
code @Common : {
Text : name,
TextArrangement : #TextOnly
} @title : '{i18n>Category}'
};

annotate service.Priority with {
code @Common : {
Text : name,
TextArrangement : #TextOnly
} @title : '{i18n>Priority}'
};

annotate service.IncidentStatus with {
code @Common : {
Text : name,
TextArrangement : #TextOnly
} @title : '{i18n>IncidentStatus}'
};

annotate service.Incidents with @(
Aggregation.ApplySupported : {
$Type : 'Aggregation.ApplySupportedType',
Transformations : [
'aggregate',
'topcount',
'bottomcount',
'identity',
'concat',
'groupby',
'filter',
'expand',
'top',
'skip',
'orderby',
'search'
],
GroupableProperties : [
category_code
],
AggregatableProperties : [
{
$Type : 'Aggregation.AggregatablePropertyType',
Property : ID
}
]
},
Analytics.AggregatedProperties : [{
Name : 'IncidentsPerCategory',
AggregationMethod : 'countdistinct',
AggregatableProperty : ID,
![@Common.Label] : '{i18n>IncidentsPerCategory}'
}]
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using scp.cloud from '../db/schema';

service IncidentService {

entity Incidents as projection on cloud.Incidents;

entity IncidentFlow as projection on cloud.IncidentFlow;

entity IncidentProcessTimeline as projection on cloud.IncidentProcessTimeline;

entity ProcessingThreshold as projection on cloud.ProcessingThreshold;

entity Individual as projection on cloud.Individual;

entity Category as projection on cloud.Category;

entity Priority as projection on cloud.Priority;

}
20 changes: 19 additions & 1 deletion packages/fiori-annotation-api/test/unit/fiori-service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,25 @@ describe('fiori annotation service', () => {
expect(serialize(metadata.schema, PROJECTS.V4_CDS_START.root)).toMatchSnapshot();
});

test('new file from memfs', async () => {
test('new file from memfs, no apps', async () => {
const project = PROJECTS.V4_CAP_NO_APPS;
const root = project.root;
const fsEditor = await createFsEditorForProject(root);
const servicesFilePath = pathFromUri(project.files.services);
const newFileName = 'new-file';
const appName = 'incidents';
const testData = `using from './${appName}/${newFileName}';\n`;
const annotationFilePath = join(root, 'app', appName, `${newFileName}.cds`);
fsEditor.write(annotationFilePath, '');
fsEditor.write(servicesFilePath, testData);

const service = await testRead(project.root, [], 'IncidentService', fsEditor);
const annotations = service.getSchema().schema.annotations;

expect(annotations[pathToFileURL(servicesFilePath).toString()]).toHaveLength(0);
expect(annotations[pathToFileURL(annotationFilePath).toString()]).toHaveLength(0);
});
test('new file from memfs, existing service file', async () => {
const project = PROJECTS.V4_CDS_START;
const root = project.root;
const fsEditor = await createFsEditorForProject(root);
Expand Down
Loading

0 comments on commit d65559e

Please sign in to comment.