cap-domain 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/.gitattributes +2 -0
  2. package/README.md +239 -0
  3. package/cds-plugin.js +5 -0
  4. package/index.js +33 -0
  5. package/package.json +25 -0
  6. package/src/FieldControls.js +356 -0
  7. package/src/Handlers.js +273 -0
  8. package/src/ServiceParser.js +56 -0
  9. package/src/SymbolHelper.js +125 -0
  10. package/src/Utils.js +61 -0
  11. package/src/cdsPluginImpl.js +22 -0
  12. package/src/defaultAnnotationValues.js +44 -0
  13. package/tests/.cdsrc.json +1 -0
  14. package/tests/.vscode/extensions.json +18 -0
  15. package/tests/.vscode/launch.json +18 -0
  16. package/tests/.vscode/settings.json +11 -0
  17. package/tests/.vscode/tasks.json +25 -0
  18. package/tests/_i18n/i18n.properties +5 -0
  19. package/tests/db/data/my.bookshop-Authors.csv +3 -0
  20. package/tests/db/data/my.bookshop-Books.csv +3 -0
  21. package/tests/db/data/my.bookshop-Details.csv +3 -0
  22. package/tests/db/data/my.bookshop-Request.csv +3 -0
  23. package/tests/db/data-model.cds +37 -0
  24. package/tests/eslint.config.js +6 -0
  25. package/tests/jest.config.js +9 -0
  26. package/tests/package.json +35 -0
  27. package/tests/srv/@FCDefinitions/Authors.js +24 -0
  28. package/tests/srv/@FCDefinitions/Books.js +23 -0
  29. package/tests/srv/@FCDefinitions/Details.js +16 -0
  30. package/tests/srv/@FCDefinitions/Request.js +22 -0
  31. package/tests/srv/CatalogService-annotations.cds +44 -0
  32. package/tests/srv/CatalogService.cds +46 -0
  33. package/tests/srv/CatalogService.js +21 -0
  34. package/tests/srv/_i18n/i18n_de.properties +6 -0
  35. package/tests/srv/_i18n/i18n_en.properties +6 -0
  36. package/tests/srv/cat-fiori-service.cds +7 -0
  37. package/tests/test/UsersApi.js +97 -0
  38. package/tests/test/authors.test.js +26 -0
  39. package/tests/test/books.test.js +97 -0
  40. package/tests/test/request.test.js +39 -0
  41. package/tests/test/setup.js +54 -0
@@ -0,0 +1,273 @@
1
+ const { getEntityName, createMockEntity } = require('./Utils.js');
2
+ const { getEntityFC } = require('./SymbolHelper');
3
+
4
+ /**
5
+ * Bind field control handlers to a field control entity instance.
6
+ * @param {object} fc The field control instance.
7
+ * @returns {object} Object with bound handlers
8
+ */
9
+ function bindHandlers(fc) {
10
+ /**
11
+ * Provides structured errors to the request based on a given prefix.
12
+ * @param {object} req The CDS request object.
13
+ * @param {Array<object>} errors An array of error objects, each with at least `fieldName` and `message` properties.
14
+ * @param {object} csnEntity The CSN entity definition.
15
+ * @param {string} [targetPrefix] Optional prefix for the error target. If not provided, it will be calculated.
16
+ */
17
+ function provideErrors(req, errors, csnEntity, targetPrefix) {
18
+ if (!errors || !errors.length) {
19
+ return;
20
+ }
21
+
22
+ /**
23
+ * Builds a property string from an object of descriptors.
24
+ * @param {object} descriptors An object where keys are property names and values are objects with a `value` property.
25
+ * @returns {string} A comma-separated string of key=value pairs.
26
+ */
27
+ function buildPropertyString(descriptors) {
28
+ /**
29
+ * @param {string} key Entity field
30
+ */
31
+ function isStringType(key) {
32
+ return csnEntity.elements[key].type === 'cds.String';
33
+ }
34
+
35
+ const keys = Object.entries(descriptors);
36
+
37
+ if (keys.length === 1) {
38
+ return keys.map(([, desc]) => desc.value).at(0);
39
+ }
40
+
41
+ return keys.map(([key, desc]) => isStringType(key) ? `${key}='${desc.value}'` : `${key}=${desc.value}`)
42
+ .join(',');
43
+ }
44
+
45
+ /**
46
+ * Calculates the prefix for error targets based on request parameters and entity name.
47
+ * @returns {string} The calculated prefix.
48
+ */
49
+ function calculatePrefix() {
50
+ const params = buildPropertyString(Object.getOwnPropertyDescriptors(req.params[0]));
51
+
52
+ const entityName = getEntityName(csnEntity);
53
+
54
+ return `/${entityName}(${params})`;
55
+ }
56
+
57
+ const prefix = targetPrefix || calculatePrefix();
58
+
59
+ errors.forEach(({ fieldName, message }) => {
60
+ req.error({
61
+ target: `${prefix}/${fieldName}`,
62
+ message,
63
+ description: message,
64
+ code: 400
65
+ });
66
+ });
67
+ }
68
+
69
+ /**
70
+ * Validates data with field controls, merging with a database record and calculating field controls.
71
+ * @param {object} req The CDS request object.
72
+ * @param {object} dataForValidation The data to be validated.
73
+ * @param {object} context The context object.
74
+ * @returns {Promise<object>} An object containing the original database record, the database record with virtual updates and calculated field controls, and any validation errors.
75
+ */
76
+ async function validateWithFCs(req, dataForValidation, context) {
77
+ const dbRecord = req.params.at(0) ? await SELECT.one.from(req.target).where(req.params.at(0)) : null;
78
+
79
+ const mergedRecord = Object.assign({}, dbRecord, dataForValidation);
80
+ const dbRecordWithVirtualUpdate = await fc.calculateFieldControls(mergedRecord, req, context);
81
+
82
+ // Mock defaults only make sense when there is no persisted record yet (CREATE);
83
+ // for an existing record, unrelated untouched fields must keep their real values.
84
+ const diffForValidation = fc.configuration.liveValidations && !dbRecord
85
+ ? Object.assign(createMockEntity(req.target.name), dataForValidation)
86
+ : mergedRecord;
87
+ const { errors, fieldControlAnnotationValues } = await fc.validatePayload(dbRecordWithVirtualUpdate, diffForValidation, dataForValidation, dbRecord || {});
88
+
89
+ return {
90
+ dbRecord,
91
+ dbRecordWithVirtualUpdate,
92
+ errors,
93
+ fieldControlAnnotationValues
94
+ };
95
+ }
96
+
97
+ // 1. Calculate FC for the DB record
98
+ // 2. Merge DB record with changes and calculate FCs
99
+ // 3. Apply validations
100
+ // 4. Compare FC from steps 1 and 2
101
+ // 5. Erase Fields which changed their FCs from Optional/Mandatory to Readonly/Hidden
102
+ /**
103
+ * Handles the UPDATE operation, including field control calculation, validation, and saving.
104
+ * @param {object} req The CDS request object.
105
+ * @param {Function} next The next middleware function.
106
+ * @param {object} context The context object.
107
+ * @returns {Promise<object>} The record after applying field control calculations.
108
+ */
109
+ async function UPDATEHandler(req, next, context) {
110
+ const { errors, dbRecordWithVirtualUpdate, fieldControlAnnotationValues } = await validateWithFCs(req, req.data, context);
111
+
112
+ fc.configuration.liveValidations && provideErrors(req, errors, fc.csnEntity);
113
+
114
+ fc.eraseUnavailableDynamicFields(dbRecordWithVirtualUpdate, req.data, fc.configuration, fieldControlAnnotationValues);
115
+
116
+ await fc.callOnBeforeSave(dbRecordWithVirtualUpdate, req, context);
117
+
118
+ const record = await next();
119
+
120
+ return await fc.calculateFieldControls(record, req, context);
121
+ }
122
+
123
+ /**
124
+ * Handles the DRAFT_PREPARE operation, including field control calculation and validation for draft entities.
125
+ * @param {object} req The CDS request object.
126
+ * @param {Function} next The next middleware function.
127
+ * @returns {Promise<object>} The record after applying field control calculations for the draft.
128
+ */
129
+ async function DRAFTPrepareHandler(req, next) {
130
+ const dbRecord = await SELECT.one.from(req.target).where(req.params.at(0));
131
+
132
+ const dbRecordWithFCs = await fc.calculateFieldControls(dbRecord, req);
133
+
134
+ const dbRecordWithVirtualUpdate = await fc.calculateFieldControls(Object.assign({}, dbRecord, req.data), req);
135
+
136
+ const { errors } = await fc.validatePayload(dbRecordWithFCs, dbRecordWithFCs);
137
+
138
+ provideErrors(req, errors, fc.csnEntity, 'in');
139
+
140
+ await fc.callOnBeforeSave(dbRecordWithVirtualUpdate, req);
141
+
142
+ const record = await next();
143
+
144
+ await fc.callOnAfterSave(dbRecordWithVirtualUpdate, req);
145
+
146
+ return await fc.calculateFieldControls(record, req);
147
+ }
148
+
149
+ /**
150
+ * Handles the READ operation, applying field control calculations to the entity data.
151
+ * @param {object} entity The entity data being read.
152
+ * @param {object} req The CDS request object.
153
+ * @param {object} context The context object.
154
+ * @returns {Promise<object>} The entity data after applying field control calculations.
155
+ */
156
+ async function READHandler(entity, req, context) {
157
+ return await fc.calculateFieldControls(entity, req, context);
158
+ }
159
+
160
+ /**
161
+ * Handles the CREATE_DRAFT operation, applying field control calculations to the newly created draft record.
162
+ * @param {object} req The CDS request object.
163
+ * @param {Function} next The next middleware function.
164
+ * @returns {Promise<object>} The draft record after applying field control calculations.
165
+ */
166
+ async function CreateDraftHandler(req, next) {
167
+ const record = await next();
168
+
169
+ return await fc.calculateFieldControls(record, req);
170
+ }
171
+
172
+ return {
173
+ UPDATEHandler,
174
+ DRAFTPrepareHandler,
175
+ READHandler,
176
+ CreateDraftHandler,
177
+ validateWithFCs,
178
+ provideErrors
179
+ };
180
+ }
181
+
182
+ /**
183
+ * Bind field control handlers to a field control instance for a specific entity.
184
+ * @param {string} csnEntity CSN entity definition
185
+ * @returns {object} Object with bound handlers
186
+ */
187
+ function bindEntityHandlers(csnEntity) {
188
+ const fc = getEntityFC(csnEntity);
189
+
190
+ return bindHandlers(fc);
191
+ }
192
+
193
+ /**
194
+ * Provide structured errors to the request.
195
+ * @param {object} req The CDS request object.
196
+ * @param {Array} errors Array of error objects.
197
+ * @param {string} targetPrefix Error target prefix.x
198
+ */
199
+ function provideErrors(req, errors, targetPrefix) {
200
+ const csnEntity = req.target;
201
+ const { provideErrors } = bindEntityHandlers(csnEntity);
202
+
203
+ provideErrors(req, errors, csnEntity, targetPrefix);
204
+ }
205
+
206
+ /**
207
+ * Throw errors and stop request if errors exist.
208
+ * @param {object} req The CDS request object.
209
+ * @param {Array} args Error arguments.
210
+ */
211
+ function throwErrorsAndStopIfExists(req, ...args) {
212
+ provideErrors(req, ...args);
213
+
214
+ if (req?.errors?.length) {
215
+ req.reject();
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Validate data with field controls and return errors.
221
+ * @param {object} req The CDS request object.
222
+ * @param {object} dataForValidation Data to validate.
223
+ * @param {object} context Context
224
+ * @returns {Promise<Array>} Array of validation errors.
225
+ */
226
+ async function validateWithFCs(req, dataForValidation, context) {
227
+ const { validateWithFCs } = bindEntityHandlers(req.target);
228
+
229
+ return await validateWithFCs(req, dataForValidation, context);
230
+ }
231
+
232
+ module.exports = {
233
+ async calculateFieldControls(data, req, { csnEntity, context = {} }) {
234
+ const fc = getEntityFC(csnEntity || req.target);
235
+
236
+ return await fc.calculateFieldControls(data, req, { context });
237
+ },
238
+ bindHandlers,
239
+ bindEntityHandlers,
240
+ async execAfterREADHandler(entity, req, context) {
241
+ const { READHandler } = bindEntityHandlers(req.target);
242
+
243
+ return await READHandler(entity, req, context);
244
+ },
245
+
246
+ async execUPDATEHandler(req, next, context) {
247
+ const { UPDATEHandler } = bindEntityHandlers(req.target);
248
+
249
+ return await UPDATEHandler(req, next, context);
250
+ },
251
+
252
+ validateWithFCs,
253
+
254
+ async validateAndThowErrorsIfExists(req, dataForValidation, targetPrefix, context) {
255
+ const { dbRecord, errors } = await validateWithFCs(req, dataForValidation, context);
256
+
257
+ throwErrorsAndStopIfExists(req, errors, targetPrefix);
258
+
259
+ return dbRecord;
260
+ },
261
+
262
+ async validateAndAttachErrors(req, dataForValidation, targetPrefix, context) {
263
+ const { dbRecord, errors } = await validateWithFCs(req, dataForValidation, context);
264
+
265
+ provideErrors(req, errors, targetPrefix);
266
+
267
+ return { dbRecord, errors };
268
+ },
269
+
270
+ provideErrors,
271
+
272
+ throwErrorsAndStopIfExists
273
+ };
@@ -0,0 +1,56 @@
1
+ const defaultAnnotationValues = require('./defaultAnnotationValues');
2
+
3
+ /**
4
+ * Extract FCSettings annotations from an annotation object.
5
+ * @param {object} annotations The annotation object.
6
+ * @returns {object|null} Settings object
7
+ */
8
+ function extractFCSettings(annotations) {
9
+ if (!annotations) {
10
+ return null;
11
+ }
12
+
13
+ const FCSettings = {};
14
+
15
+ for (const [ key, value ] of Object.entries(annotations)) {
16
+ if (key.startsWith('@FCSettings.')) {
17
+ const path = key.slice('@FCSettings.'.length).split('.');
18
+ let current = FCSettings;
19
+
20
+ for (let i = 0; i < path.length - 1; i++) {
21
+ const segment = path[i];
22
+
23
+ if (!(segment in current)) {
24
+ current[segment] = {};
25
+ }
26
+
27
+ current = current[segment];
28
+ }
29
+
30
+ current[path[path.length - 1]] = value;
31
+ }
32
+ }
33
+
34
+ return Object.keys(FCSettings).length > 0
35
+ ? { ...structuredClone(defaultAnnotationValues), ...FCSettings }
36
+ : null;
37
+ }
38
+
39
+ module.exports = class ServiceParser {
40
+ /**
41
+ * Call a callback for each FC entity in the given services.
42
+ * @param {Array|object} services The services to process.
43
+ * @param {Function} callback The callback to call for each FC entity.
44
+ */
45
+ static onEachFCEntity(services, callback) {
46
+ for (const srv of services) {
47
+ if (srv instanceof cds.ApplicationService) {
48
+ Object.values(srv.entities).forEach((entity) => {
49
+ const FCSettings = extractFCSettings(entity?.$flatAnnotations);
50
+
51
+ FCSettings && FCSettings.path && callback(srv, entity, FCSettings);
52
+ });
53
+ }
54
+ }
55
+ }
56
+ };
@@ -0,0 +1,125 @@
1
+ const actions = {
2
+ onBeforeSave: Symbol('onBeforeSave'),
3
+ onAfterSave: Symbol('onAfterSave'),
4
+ setOnBeforeCalculateFC: Symbol('setOnBeforeCalculateFC'),
5
+ srvEntitiesFC: Symbol('srvEntitiesFC'),
6
+ entityFC: Symbol('entityFC')
7
+ };
8
+
9
+ /**
10
+ * Set the onBeforeSave handler.
11
+ * @param {object} obj The configuration object.
12
+ * @param {Function} handler The handler function.
13
+ */
14
+ function setOnBeforeSave(obj, handler) {
15
+ obj[actions.onBeforeSave] = handler;
16
+ }
17
+
18
+ /**
19
+ * Get the onBeforeSave handler.
20
+ * @param {object} obj The configuration object.
21
+ * @returns {object|null} Field Control object definition
22
+ */
23
+ function getOnBeforeSave(obj) {
24
+ return obj[actions.onBeforeSave];
25
+ }
26
+
27
+ /**
28
+ * Get the onAfterSave handler.
29
+ * @param {object} obj The configuration object.
30
+ * @returns {object|null} Field Control object definition
31
+ */
32
+ function getOnAfterSave(obj) {
33
+ return obj[actions.onAfterSave];
34
+ }
35
+
36
+ /**
37
+ * Set the onAfterSave handler.
38
+ * @param {object} obj The configuration object.
39
+ * @param {Function} handler The handler function.
40
+ */
41
+ function setOnAfterSave(obj, handler) {
42
+ obj[actions.onAfterSave] = handler;
43
+ }
44
+
45
+ /**
46
+ * Set the onBeforeCalculateFC handler.
47
+ * @param {object} obj The configuration object.
48
+ * @param {Function} handler The handler function.
49
+ */
50
+ function setOnBeforeCalculateFC(obj, handler) {
51
+ obj[actions.setOnBeforeCalculateFC] = handler;
52
+ }
53
+
54
+ /**
55
+ * Get the onBeforeCalculateFC handler.
56
+ * @param {object} obj The configuration object.
57
+ * @returns {object|null} Field Control object definition
58
+ */
59
+ function getOnBeforeCalculateFC(obj) {
60
+ return obj[actions.setOnBeforeCalculateFC];
61
+ }
62
+
63
+ /**
64
+ * Set the entity field control handler.
65
+ * @param {object} obj The configuration object.
66
+ * @param {Function} handler The handler function.
67
+ */
68
+ function setEntityFC(obj, handler) {
69
+ obj[actions.entityFC] = handler;
70
+ }
71
+
72
+ /**
73
+ * Get the entity field control handler.
74
+ * @param {object} obj The configuration object.
75
+ * @returns {object|null} Field Control object definition
76
+ */
77
+ function getEntityFC(obj) {
78
+ const fcObject = obj[actions.entityFC];
79
+
80
+ if (!fcObject) {
81
+ throw Error(`CSN Entity: ${ obj.name } doesn't have configuration, check annotations definitions`);
82
+ }
83
+
84
+ return fcObject;
85
+ }
86
+
87
+ /**
88
+ * Get the service entities field controls.
89
+ * @param {object} obj The configuration object.
90
+ * @returns {object|null} Field Control object definition
91
+ */
92
+ function getSrvEntitiesFCs(obj) {
93
+ return obj[actions.srvEntitiesFC];
94
+ }
95
+
96
+ /**
97
+ * Add service entities field controls.
98
+ * @param {object} obj The configuration object.
99
+ * @param {object} handler The handler object.
100
+ */
101
+ function addSrvEntitiesFCs(obj, handler) {
102
+ const reference = obj[actions.srvEntitiesFC];
103
+
104
+ if (!reference) {
105
+ obj[actions.srvEntitiesFC] = handler;
106
+ } else {
107
+ Object.assign(reference, handler);
108
+ }
109
+ }
110
+
111
+ module.exports = {
112
+ setOnBeforeSave,
113
+ getOnBeforeSave,
114
+ setOnBeforeCalculateFC,
115
+ getOnBeforeCalculateFC,
116
+
117
+ getSrvEntitiesFCs,
118
+ addSrvEntitiesFCs,
119
+
120
+ getOnAfterSave,
121
+ setOnAfterSave,
122
+
123
+ setEntityFC,
124
+ getEntityFC
125
+ };
package/src/Utils.js ADDED
@@ -0,0 +1,61 @@
1
+ function formatMessage(msg, args) {
2
+ return args.reduce((result, val, i) => result.replace(`{${i}}`, val), msg);
3
+ }
4
+
5
+ const FIELD_TYPE_DEFAULTS = {
6
+ 'cds.Timestamp': null,
7
+ 'cds.DateTime': null,
8
+ 'cds.Date': null,
9
+ 'cds.Association': null,
10
+ 'cds.Composition': null,
11
+ 'cds.Integer': null,
12
+ 'cds.UUID': null,
13
+ 'cds.Boolean': false,
14
+ };
15
+
16
+ module.exports = class Utils {
17
+ static createMockEntity(entityName) {
18
+ const { elements } = cds.entities[entityName];
19
+
20
+ return Object.entries(elements).reduce((acc, [fieldKey, fieldInfo]) => {
21
+ const hasValueList = fieldInfo['@Common.ValueList.CollectionPath'];
22
+
23
+ acc[fieldKey] = hasValueList
24
+ ? null
25
+ : FIELD_TYPE_DEFAULTS[fieldInfo.type] ?? '';
26
+
27
+ return acc;
28
+ }, {});
29
+ }
30
+
31
+ static decorateAllFCs(configs, decorator) {
32
+ for (const key in configs) {
33
+ if (configs.hasOwnProperty(key)) {
34
+ const originalFc = configs[key].fc;
35
+
36
+ configs[key].fc = (...args) => decorator(originalFc, ...args);
37
+ }
38
+ }
39
+ }
40
+
41
+ static getEntityName(csnEntity) {
42
+ const [, entityName] = csnEntity.name.split('.');
43
+
44
+ return entityName;
45
+ }
46
+
47
+ static getText(key, args) {
48
+ const locale = cds.context.locale || 'en';
49
+ const bundle = cds.i18n.bundle4();
50
+ const texts = bundle.texts4(locale);
51
+ const msg = texts[key] || key;
52
+
53
+ return formatMessage(msg, args || []);
54
+ }
55
+
56
+ static getBoundI18nBundle() {
57
+ return {
58
+ getText: (...args) => Utils.getText(...args)
59
+ };
60
+ }
61
+ };
@@ -0,0 +1,22 @@
1
+ const path = require('path');
2
+ const ServiceParser = require('./ServiceParser.js');
3
+ const { FieldControls } = require('./FieldControls.js');
4
+ const Utils = require('./Utils.js');
5
+ const { setEntityFC, addSrvEntitiesFCs } = require('./SymbolHelper');
6
+
7
+ module.exports = function(service) {
8
+ const services = Array.isArray(service) ? service : [ service ];
9
+
10
+ ServiceParser.onEachFCEntity(services, async (srv, csnEntity, configuration) => {
11
+ const entityName = Utils.getEntityName(csnEntity);
12
+ const configurationFilePath = path.resolve('./', configuration.path);
13
+
14
+ const configurationEntity = require(configurationFilePath);
15
+
16
+ const fc = new FieldControls(srv, csnEntity, configurationEntity, configuration);
17
+
18
+ setEntityFC(csnEntity, fc);
19
+ csnEntity.drafts && setEntityFC(csnEntity.drafts, fc);
20
+ addSrvEntitiesFCs(srv, { [entityName]: fc });
21
+ });
22
+ };
@@ -0,0 +1,44 @@
1
+
2
+ /**
3
+ * Available annotation examples.
4
+ * @FCSettings.liveValidations: true
5
+ * @FCSettings.autoErase: true
6
+ * @FCSettings.path: 'srv/@FCDefinitions/InternalUsersForm.js'
7
+ * @FCSettings: {
8
+ * useImpl.BeneficiaryAddress: 'Addresses',
9
+ * useImpl.DeliveryAddress: 'Addresses',
10
+ * }
11
+ * or
12
+ * @FCSettings: {
13
+ * useImpl: {
14
+ * BeneficiaryAddress: 'Addresses',
15
+ * DeliveryAddress: 'Addresses',
16
+ * }
17
+ * }
18
+ */
19
+
20
+ /**
21
+ * Cds configs:
22
+ *
23
+ * "cds": {
24
+ * "enable:capdomain:plugin": true,
25
+ * "enable:capdomain:liveValidations": false,
26
+ * "enable:capdomain:autoErase": false,
27
+ * "enable:capdomain:defaultFCValue": 3,
28
+ * "enable:capdomain:blockUnannotatedValueChanges": true,
29
+ * }
30
+ */
31
+
32
+ /**
33
+ * Merge logic of configurations should work in the following way
34
+ * Object.assing({}, defaultLibValues, cdsConfiguration, annotationConfiguration, codeCallConfigurations)
35
+ */
36
+
37
+ const FCSettings = {
38
+ autoErase: cds.env['enable:capdomain:autoErase'] ?? true,
39
+ liveValidations: cds.env['enable:capdomain:liveValidations'] ?? true,
40
+ blockUnannotatedValueChanges: cds.env['enable:capdomain:blockUnannotatedValueChanges'] ?? true,
41
+ useImpl: {}
42
+ };
43
+
44
+ module.exports = FCSettings;
@@ -0,0 +1 @@
1
+ {}
@@ -0,0 +1,18 @@
1
+ {
2
+ // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
3
+ // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
4
+
5
+ // List of extensions which should be recommended for users of this workspace.
6
+ "recommendations": [
7
+ "SAPSE.vscode-cds",
8
+ "dbaeumer.vscode-eslint",
9
+ "esbenp.prettier-vscode",
10
+ "mechatroner.rainbow-csv",
11
+ "qwtel.sqlite-viewer",
12
+ "humao.rest-client"
13
+ ],
14
+ // List of extensions recommended by VS Code that should not be recommended for users of this workspace.
15
+ "unwantedRecommendations": [
16
+
17
+ ]
18
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "version": "0.2.0",
3
+ "configurations": [
4
+ {
5
+ "name": "cds serve",
6
+ "request": "launch",
7
+ "type": "node",
8
+ "cwd": "${workspaceFolder}",
9
+ "runtimeExecutable": "cds",
10
+ "args": [
11
+ "serve",
12
+ "--with-mocks",
13
+ "--in-memory?"
14
+ ],
15
+
16
+ }
17
+ ]
18
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ // uncomment entries once all libraries have been installed via 'npm install'
3
+ "eslint.validate": [
4
+ // "cds",
5
+ // "csn",
6
+ // "csv",
7
+ // "csv (semicolon)",
8
+ // "tab",
9
+ // "tsv"
10
+ ]
11
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
3
+ // for the documentation about the tasks.json format
4
+ "version": "2.0.0",
5
+ "tasks": [
6
+ {
7
+ "type": "shell",
8
+ "label": "cds watch",
9
+ "command": "cds",
10
+ "args": ["watch"],
11
+ "group": {
12
+ "kind": "build",
13
+ "isDefault": true
14
+ },
15
+ "problemMatcher": []
16
+ },
17
+ {
18
+ "type": "shell",
19
+ "label": "cds serve",
20
+ "command": "cds",
21
+ "args": ["serve", "--with-mocks", "--in-memory?"],
22
+ "problemMatcher": []
23
+ }
24
+ ]
25
+ }
@@ -0,0 +1,5 @@
1
+ previewDate=Preview Date has to after today
2
+ dateToday-errorMessage= Date has to be greater than today
3
+ price-error = Price must be set
4
+ statusCheck-Cancelled = Please use remaining stock before cancelling
5
+ statusCheck-Released = Release Date must be today: {{custom-today}}
@@ -0,0 +1,3 @@
1
+ ID;name;
2
+ 1;Alex
3
+ 2;Pavel
@@ -0,0 +1,3 @@
1
+ ID;title;stock;enableDetails;Author_ID
2
+ 1;Wuthering Heights;100;true;1
3
+ 2;Jane Eyre;500;false;2
@@ -0,0 +1,3 @@
1
+ ID;title;
2
+ 1;detail name 1;
3
+ 2;detail name 2;
@@ -0,0 +1,3 @@
1
+ ID;title;Detail_ID;
2
+ 1;Adjsut height;1;
3
+ 2;Adjust weight;2;