@sap/cds 1.15.0 → 1.17.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.
package/metadata.js CHANGED
@@ -1,26 +1,25 @@
1
- var async = require('async');
1
+ const async = require('async');
2
2
 
3
- var transaction = require('./transaction');
4
- var manager = require('./manager');
5
- var utils = require('./utils');
6
- var Queue = require('./util/Queue');
7
- var queries = require("./cds-queries");
8
- var SqlQuery = queries.Query;
3
+ const transaction = require('./transaction');
4
+ const utils = require('./utils');
5
+ const Queue = require('./util/Queue');
6
+ const queries = require('./cds-queries');
7
+ const SqlQuery = queries.Query;
9
8
 
10
- var logger = utils.logger;
9
+ const logger = utils.logger;
11
10
 
12
11
 
13
- /// entity management ///////////////////////////////////////////////////////
12
+ // / entity management ///////////////////////////////////////////////////////
14
13
 
15
14
  // known resolved entities
16
- var knownEntities = {};
15
+ let knownEntities = {};
17
16
 
18
17
  // pending callbacks for async getEntity()
19
- var importNotifications = {};
18
+ const importNotifications = {};
20
19
 
21
20
  // metadata cache
22
- var sqlMetadata = {};
23
- var cdsMetadata = {};
21
+ const sqlMetadata = {};
22
+ const cdsMetadata = {};
24
23
 
25
24
 
26
25
  /* import list of entities
@@ -37,298 +36,327 @@ var cdsMetadata = {};
37
36
  */
38
37
 
39
38
  // central import request queue for serializing imports
40
- var importQueue = new Queue.Queue();
41
-
42
- exports._import = function (refs, opts, callback) {
43
- // work in progress: entities and dependencies to import
44
- var pool = {};
45
-
46
- // request import of dependent associated entities
47
- function addDepImports(entity) {
48
- utils.forStruct(entity.$_mapping, {
49
- $association: function(s, f, p) {
50
- var name = s[f].$association.$entity;
51
- var schema = opts.$schema || s[f].$association.$schema;
52
- todo.push({ $entity: name, $schema: schema,
53
- $options: { $auto: true } });
54
- logger.debug("node-cds import: added dependency " +
55
- entity.$_metadata.entityName + " -> " + name);
56
- if (s[f].$association.$viaEntity) {
57
- name = s[f].$association.$viaEntity;
58
- todo.push({$entity: name, $schema: schema,
59
- $options: {$unmanaged: true, $auto: true}});
60
- logger.debug("node-cds import: added dependency " +
61
- entity.$_metadata.entityName + " -> " + name);
62
- }
63
- }
64
- });
65
- }
39
+ const importQueue = new Queue.Queue();
66
40
 
67
- // main metadata query function
68
- function query(entityName, ref, callback) {
69
- var cdsFullname = ref.$entity;
70
- var tableName = ref.$table || cdsFullname;
71
- var schemaName = ref.$schema || ""; // '"' + ref.$schema + '".' : "";
72
- var fields = ref.$fields || {};
73
- var options = ref.$options || {};
74
-
75
- if (!entityName)
76
- return callback("Missing entity name");
77
- if (!tableName)
78
- return callback("Unknown entity table");
79
-
80
- if (ref.$table && !ref.$entity)
81
- options.$noCds = true;
82
- if (ref.$unmanaged)
83
- options.$unmanaged = true;
84
-
85
- // retrieve metadata and build entity
86
- logger.debug("node-cds import: query metadata for " + schemaName + "." + tableName);
87
- getSqlMetadata(tableName, schemaName, function(err, sqlMetadata) {
88
- if (err)
89
- return callback("Error importing " + cdsFullname + ": " + err);
90
- getCdsMetadata(cdsFullname, schemaName, options, function(err, cdsMetadata) {
91
- if (err)
92
- return callback("Error importing " + cdsFullname + ": " + err);
93
- if (entityName in pool)
94
- throw new Error("*** ASSERT FAIL *** conflicting entity in pool");
95
- var entity = makeEntity(entityName, tableName, schemaName, fields,
96
- sqlMetadata, cdsMetadata, options);
97
- if (typeof entity === "string")
98
- return callback(entity); // actually, we got an error
99
- pool[entityName] = entity;
100
- logger.debug("node-cds import: added " + entityName + " to wip pool");
101
- addDepImports(entity); // recursively import target entities
102
- return callback(null);
103
- });
104
- });
105
- }
41
+ exports._import = function(refs, opts, callback) {
42
+ // work in progress: entities and dependencies to import
43
+ const pool = {};
106
44
 
107
- // top level: request queue handling
108
- var todo = [];
109
- for (var i in refs) {
110
- var r = utils.shallowCopy(refs[i]);
111
- for (var p in opts) // merge in global options
112
- if (!(p in r))
113
- r[p] = opts[p];
114
- todo.push(r);
45
+ // request import of dependent associated entities
46
+ function addDepImports(entity) {
47
+ utils.forStruct(entity.$_mapping, {
48
+ $association: function(s, f, p) {
49
+ let name = s[f].$association.$entity;
50
+ const schema = opts.$schema || s[f].$association.$schema;
51
+ todo.push({$entity: name, $schema: schema,
52
+ $options: {$auto: true}});
53
+ logger.debug(`node-cds import: added dependency ${
54
+ entity.$_metadata.entityName } -> ${ name}`);
55
+ if (s[f].$association.$viaEntity) {
56
+ name = s[f].$association.$viaEntity;
57
+ todo.push({$entity: name, $schema: schema,
58
+ $options: {$unmanaged: true, $auto: true}});
59
+ logger.debug(`node-cds import: added dependency ${
60
+ entity.$_metadata.entityName } -> ${ name}`);
61
+ }
62
+ },
63
+ });
64
+ }
65
+
66
+ // main metadata query function
67
+ function query(entityName, ref, callback) {
68
+ const cdsFullname = ref.$entity;
69
+ const tableName = ref.$table || cdsFullname;
70
+ const schemaName = ref.$schema || ''; // '"' + ref.$schema + '".' : "";
71
+ const fields = ref.$fields || {};
72
+ const options = ref.$options || {};
73
+
74
+ if (!entityName) {
75
+ return callback('Missing entity name');
115
76
  }
116
-
117
- function start(callback) {
118
- async.whilst(pending, loop, callback);
77
+ if (!tableName) {
78
+ return callback('Unknown entity table');
119
79
  }
120
80
 
121
- function pending() {
122
- return todo.length > 0;
81
+ if (ref.$table && !ref.$entity) {
82
+ options.$noCds = true;
83
+ }
84
+ if (ref.$unmanaged) {
85
+ options.$unmanaged = true;
123
86
  }
124
87
 
125
- function loop(callback) {
126
- var item = todo.shift();
127
- if (!item)
128
- throw new Error("*** ASSERT FAIL *** empty todo list");
129
- var entityName = item.$name || item.$entity || item.$table;
130
- logger.debug("node-cds import loop: processing " + entityName);
131
-
132
- // choke recursive imports
133
- var previously = pool[entityName] || knownEntities[entityName];
134
- if (previously) {
135
- logger.debug("node-cds import: " + entityName + " already known");
136
- if (!(entityName in pool))
137
- pool[entityName] = previously; // present to callback
138
- var options = item.$options || {};
139
- if (!options.$auto) {
140
- updateEntity(previously, item.$fields || {}, options);
141
- addDepImports(previously);
142
- }
143
- return callback(null);
88
+ // retrieve metadata and build entity
89
+ logger.debug(`node-cds import: query metadata for ${ schemaName }.${ tableName}`);
90
+ getSqlMetadata(tableName, schemaName, function(err, sqlMetadata) {
91
+ if (err) {
92
+ return callback(`Error importing ${ cdsFullname }: ${ err}`);
93
+ }
94
+ getCdsMetadata(cdsFullname, schemaName, options, function(err, cdsMetadata) {
95
+ if (err) {
96
+ return callback(`Error importing ${ cdsFullname }: ${ err}`);
97
+ }
98
+ if (entityName in pool) {
99
+ throw new Error('*** ASSERT FAIL *** conflicting entity in pool');
144
100
  }
101
+ const entity = makeEntity(entityName, tableName, schemaName, fields,
102
+ sqlMetadata, cdsMetadata, options);
103
+ if (typeof entity === 'string') {
104
+ return callback(entity);
105
+ } // actually, we got an error
106
+ pool[entityName] = entity;
107
+ logger.debug(`node-cds import: added ${ entityName } to wip pool`);
108
+ addDepImports(entity); // recursively import target entities
109
+ return callback(null);
110
+ });
111
+ });
112
+ }
113
+
114
+ // top level: request queue handling
115
+ const todo = [];
116
+ for (const i in refs) {
117
+ const r = utils.shallowCopy(refs[i]);
118
+ // merge in global options
119
+ for (const p in opts) {
120
+ if (!(p in r)) {
121
+ r[p] = opts[p];
122
+ }
123
+ }
124
+ todo.push(r);
125
+ }
126
+
127
+ function start(callback) {
128
+ async.whilst(pending, loop, callback);
129
+ }
145
130
 
146
- query(entityName, item, callback);
131
+ function pending() {
132
+ return todo.length > 0;
133
+ }
134
+
135
+ function loop(callback) {
136
+ const item = todo.shift();
137
+ if (!item) {
138
+ throw new Error('*** ASSERT FAIL *** empty todo list');
139
+ }
140
+ const entityName = item.$name || item.$entity || item.$table;
141
+ logger.debug(`node-cds import loop: processing ${ entityName}`);
142
+
143
+ // choke recursive imports
144
+ const previously = pool[entityName] || knownEntities[entityName];
145
+ if (previously) {
146
+ logger.debug(`node-cds import: ${ entityName } already known`);
147
+ if (!(entityName in pool)) {
148
+ pool[entityName] = previously;
149
+ } // present to callback
150
+ const options = item.$options || {};
151
+ if (!options.$auto) {
152
+ updateEntity(previously, item.$fields || {}, options);
153
+ addDepImports(previously);
154
+ }
155
+ return callback(null);
147
156
  }
148
157
 
149
- function done(err) {
150
- if (err)
151
- return callback(err, []);
158
+ query(entityName, item, callback);
159
+ }
152
160
 
153
- // add XS interface and wire up associations in wip skeletons, add to cache
154
- for (var i in pool) {
155
- if (!pool[i].$_metadata.isResolved) {
156
- resolveEntity(pool[i], pool);
157
- registerEntity(pool[i]);
158
- }
159
- logger.debug("node-cds import: " + pool[i].$_metadata.entityName + " imported");
160
- }
161
+ function done(err) {
162
+ if (err) {
163
+ return callback(err, []);
164
+ }
161
165
 
162
- // return result via wip pool
163
- return callback(null, pool);
166
+ // add XS interface and wire up associations in wip skeletons, add to cache
167
+ for (const i in pool) {
168
+ if (!pool[i].$_metadata.isResolved) {
169
+ resolveEntity(pool[i], pool);
170
+ registerEntity(pool[i]);
171
+ }
172
+ logger.debug(`node-cds import: ${ pool[i].$_metadata.entityName } imported`);
164
173
  }
165
174
 
166
- importQueue.push(start, done);
175
+ // return result via wip pool
176
+ return callback(null, pool);
177
+ }
178
+
179
+ importQueue.push(start, done);
167
180
  };
168
181
 
169
182
 
170
183
  // trigger import of all associated entities
171
- function makeEntity(entityName, tableName, schemaName, fields, sqlMetadata, cdsMetadata, options) {
172
- // prepare field mapping
173
- var sqlMapping = buildSqlMapping(sqlMetadata);
174
- var cdsMapping = buildCdsMapping(cdsMetadata);
175
- var mapping = mergeMapping(sqlMapping, cdsMapping);
176
- mapping = mergeMapping(mapping, fields);
177
- var check = checkMapping(mapping);
178
- if (check)
179
- return check; // found error
180
-
181
- // assemble entity metadata
182
- var metadata = {
183
- entityName: entityName,
184
- tableName: tableName,
185
- schemaName: schemaName,
186
- sqlMetadata: sqlMetadata,
187
- cdsMetadata: cdsMetadata,
188
- isUnmanaged: options.$unmanaged || false,
189
- isAutoImport: options.$auto || false,
190
- isResolved: false // true iff mapping contains associated entiy objects
191
- };
192
- updateMetadata(metadata, mapping);
193
-
194
- // build and register unresolved entity object
195
- entity = {
196
- $_metadata: metadata,
197
- $_mapping: mapping
198
- };
199
-
200
- // verify entity (in particular, mapping)
201
- var e = verifyEntity(entity);
202
- if (e) return e;
203
-
204
- // add query interface
205
- SqlQuery.addExpressionFunctions(entity, entity);
206
- entity.$query = function (client) {
207
- var param = {};
208
- param["t0"] = {entity: this};
209
- return new SqlQuery(client, param);
210
- };
211
-
212
- entity.$ref = function (id) {
213
- return new queries.Ref(this, id);
214
- };
215
-
216
- entity.$from = function (id) {
217
- var param = {};
218
- var e = this;
219
- param[id] = {entity: e};
220
- return new SqlQuery(null, param);
221
- };
222
-
223
- return entity;
184
+ function makeEntity(
185
+ entityName, tableName, schemaName, fields, sqlMetadata, cdsMetadata, options,
186
+ ) {
187
+ // prepare field mapping
188
+ const sqlMapping = buildSqlMapping(sqlMetadata);
189
+ const cdsMapping = buildCdsMapping(cdsMetadata);
190
+ let mapping = mergeMapping(sqlMapping, cdsMapping);
191
+ mapping = mergeMapping(mapping, fields);
192
+ const check = checkMapping(mapping);
193
+ if (check) {
194
+ return check;
195
+ } // found error
196
+
197
+ // assemble entity metadata
198
+ const metadata = {
199
+ entityName: entityName,
200
+ tableName: tableName,
201
+ schemaName: schemaName,
202
+ sqlMetadata: sqlMetadata,
203
+ cdsMetadata: cdsMetadata,
204
+ isUnmanaged: options.$unmanaged || false,
205
+ isAutoImport: options.$auto || false,
206
+ isResolved: false, // true iff mapping contains associated entiy objects
207
+ };
208
+ updateMetadata(metadata, mapping);
209
+
210
+ // build and register unresolved entity object
211
+ const entity = {
212
+ $_metadata: metadata,
213
+ $_mapping: mapping,
214
+ };
215
+
216
+ // verify entity (in particular, mapping)
217
+ const e = verifyEntity(entity);
218
+ if (e) {
219
+ return e;
220
+ }
221
+
222
+ // add query interface
223
+ SqlQuery.addExpressionFunctions(entity, entity);
224
+ entity.$query = function(client) {
225
+ const param = {};
226
+ param['t0'] = {entity: this};
227
+ return new SqlQuery(client, param);
228
+ };
229
+
230
+ entity.$ref = function(id) {
231
+ return new queries.Ref(this, id);
232
+ };
233
+
234
+ entity.$from = function(id) {
235
+ const param = {};
236
+ const e = this;
237
+ param[id] = {entity: e};
238
+ return new SqlQuery(null, param);
239
+ };
240
+
241
+ return entity;
224
242
  }
225
243
 
226
244
  // check various entity properties
227
245
  function verifyEntity(entity) {
228
- var error = null;
229
- utils.forStruct(entity.$_mapping, {
230
- $association: function (s, f, p) {
231
- var assoc = s[f].$association;
232
- if (assoc.$viaEntity) {
233
- if (!assoc.$source)
234
- error = "Error: missing $source in viaEntity association";
235
- if (!assoc.$target)
236
- error = "Error: missing $target in viaEntity association";
237
- }
246
+ let error = null;
247
+ utils.forStruct(entity.$_mapping, {
248
+ $association: function(s, f, p) {
249
+ const assoc = s[f].$association;
250
+ if (assoc.$viaEntity) {
251
+ if (!assoc.$source) {
252
+ error = 'Error: missing $source in viaEntity association';
238
253
  }
239
- });
240
- return error;
254
+ if (!assoc.$target) {
255
+ error = 'Error: missing $target in viaEntity association';
256
+ }
257
+ }
258
+ },
259
+ });
260
+ return error;
241
261
  }
242
262
 
243
263
  // update auto-imported entity with new field data
244
264
  function updateEntity(entity, fields, options) {
245
- var entityName = entity.$_metadata.entityName;
246
- logger.debug("node-cds import: updating entity " + entityName);
247
- delete knownEntities[entityName]; // remove temporarily, still in wip pool
248
- entity.$_mapping = mergeMapping(entity.$_mapping, fields);
249
- entity.$_metadata.isResolved = false;
250
- updateMetadata(entity.$_metadata, entity.$_mapping);
251
- if (!options.$auto)
252
- entity.$_metadata.isAutoImport = false;
265
+ const entityName = entity.$_metadata.entityName;
266
+ logger.debug(`node-cds import: updating entity ${ entityName}`);
267
+ delete knownEntities[entityName]; // remove temporarily, still in wip pool
268
+ entity.$_mapping = mergeMapping(entity.$_mapping, fields);
269
+ entity.$_metadata.isResolved = false;
270
+ updateMetadata(entity.$_metadata, entity.$_mapping);
271
+ if (!options.$auto) {
272
+ entity.$_metadata.isAutoImport = false;
273
+ }
253
274
  }
254
275
 
255
276
  // update metadata information based on new mapping (in-place update)
256
277
  function updateMetadata(metadata, mapping) {
257
- var keys = {}, hasKeys = false;
258
- utils.forStruct(mapping, {
259
- $key: function(s, f, p) {
260
- if (s[f].$key) {
261
- var column = s[f].$column;
262
- keys[p + f] = {
263
- $seq: s[f].$key, //metadata.sqlMetadata[column].$key,
264
- $type: metadata.sqlMetadata[column].$type
265
- };
266
- hasKeys = true;
267
- }
268
- }
269
- });
270
- if (!hasKeys && !metadata.isUnmanaged) // need key for managing instances
271
- throw new Error("no key defined: " + metadata.entityName);
272
-
273
- var revMapping = {};
274
- utils.forStruct(mapping, {
275
- $column: function(s, f, p) {
276
- revMapping[s[f].$column] = p + f;
277
- }
278
- });
279
-
280
- metadata.keyFields = keys;
281
- metadata.revMapping = revMapping;
282
- //metadata.secondaryIndexes = [];
278
+ const keys = {}; let hasKeys = false;
279
+ utils.forStruct(mapping, {
280
+ $key: function(s, f, p) {
281
+ if (s[f].$key) {
282
+ const column = s[f].$column;
283
+ keys[p + f] = {
284
+ $seq: s[f].$key, // metadata.sqlMetadata[column].$key,
285
+ $type: metadata.sqlMetadata[column].$type,
286
+ };
287
+ hasKeys = true;
288
+ }
289
+ },
290
+ });
291
+ // need key for managing instances
292
+ if (!hasKeys && !metadata.isUnmanaged) {
293
+ throw new Error(`no key defined: ${ metadata.entityName}`);
294
+ }
295
+
296
+ const revMapping = {};
297
+ utils.forStruct(mapping, {
298
+ $column: function(s, f, p) {
299
+ revMapping[s[f].$column] = p + f;
300
+ },
301
+ });
302
+
303
+ metadata.keyFields = keys;
304
+ metadata.revMapping = revMapping;
305
+ // metadata.secondaryIndexes = [];
283
306
  }
284
307
 
285
308
  // resolve entity name references by entity classes
286
309
  function resolveEntity(entity, pool) {
287
- logger.debug("node-cds import: resolving " + entity.$_metadata.entityName);
288
- if (entity.$_metadata.isResolved)
289
- throw new Error("*** ASSERT FAIL *** resolving resolved entity: " +
290
- entity.$_metadata.entityName);
291
-
292
- function getByName(name) {
293
- var e = pool[name] || knownEntities[name];
294
- if (!e)
295
- throw new Error("*** ASSERT FAIL *** missing entity: " + name);
296
- return e;
310
+ logger.debug(`node-cds import: resolving ${ entity.$_metadata.entityName}`);
311
+ if (entity.$_metadata.isResolved) {
312
+ throw new Error(`*** ASSERT FAIL *** resolving resolved entity: ${
313
+ entity.$_metadata.entityName}`);
314
+ }
315
+
316
+ function getByName(name) {
317
+ const e = pool[name] || knownEntities[name];
318
+ if (!e) {
319
+ throw new Error(`*** ASSERT FAIL *** missing entity: ${ name}`);
297
320
  }
298
-
299
- utils.forStruct(entity.$_mapping, {
300
- $association: function(s, f, p) {
301
- var name = s[f].$association.$entity;
302
- s[f].$association.$class = getByName(name);
303
- name = s[f].$association.$viaEntity;
304
- if (name)
305
- s[f].$association.$viaClass = getByName(name);
306
- }
307
- });
308
- entity.$_metadata.isResolved = true;
321
+ return e;
322
+ }
323
+
324
+ utils.forStruct(entity.$_mapping, {
325
+ $association: function(s, f, p) {
326
+ let name = s[f].$association.$entity;
327
+ s[f].$association.$class = getByName(name);
328
+ name = s[f].$association.$viaEntity;
329
+ if (name) {
330
+ s[f].$association.$viaClass = getByName(name);
331
+ }
332
+ },
333
+ });
334
+ entity.$_metadata.isResolved = true;
309
335
  };
310
336
 
311
337
 
312
- /// other management function ///////////////////////////////////////
338
+ // / other management function ///////////////////////////////////////
313
339
 
314
340
  // return previously imported entity, or save callback if not imported yet
315
341
  exports.getEntity = function(name, callback) {
316
- logger.debug("node-cds import: getting entity " + name);
317
- if (name in knownEntities)
318
- return callback(null, knownEntities[name]);
319
- logger.debug("node-cds import: getEntity waiting for entity " + name);
320
- if (!(name in importNotifications))
321
- importNotifications[name] = [];
322
- importNotifications[name].push(callback);
342
+ logger.debug(`node-cds import: getting entity ${ name}`);
343
+ if (name in knownEntities) {
344
+ return callback(null, knownEntities[name]);
345
+ }
346
+ logger.debug(`node-cds import: getEntity waiting for entity ${ name}`);
347
+ if (!(name in importNotifications)) {
348
+ importNotifications[name] = [];
349
+ }
350
+ importNotifications[name].push(callback);
323
351
  };
324
352
 
325
353
  // sync version (used by metadata import)
326
- exports.getEntitySync = function (name) {
327
- return knownEntities[name] || null;
354
+ exports.getEntitySync = function(name) {
355
+ return knownEntities[name] || null;
328
356
  };
329
357
 
330
358
  exports.getKnownEntities = function() {
331
- return knownEntities;
359
+ return knownEntities;
332
360
  };
333
361
 
334
362
 
@@ -337,366 +365,400 @@ exports.getKnownEntities = function() {
337
365
  // For parallel imports (e.g., triggered by auto-imports), the returned entity
338
366
  // object must supercede the original object passed to registerEntity.
339
367
  function registerEntity(entity) {
340
- var name = entity.$_metadata.entityName;
341
- if (name in knownEntities)
342
- throw new Error("*** ASSERT FAIL *** trying to register known entity " + name);
343
- logger.debug("node-cds import: registering entity " + name);
344
-
345
- knownEntities[name] = entity;
346
-
347
- // check for pending requests
348
- if (name in importNotifications) {
349
- var notify = importNotifications[name];
350
- for (var i in notify)
351
- (notify[i])(null, entity);
352
- delete importNotifications[name];
368
+ const name = entity.$_metadata.entityName;
369
+ if (name in knownEntities) {
370
+ throw new Error(`*** ASSERT FAIL *** trying to register known entity ${ name}`);
371
+ }
372
+ logger.debug(`node-cds import: registering entity ${ name}`);
373
+
374
+ knownEntities[name] = entity;
375
+
376
+ // check for pending requests
377
+ if (name in importNotifications) {
378
+ const notify = importNotifications[name];
379
+ for (const i in notify) {
380
+ (notify[i])(null, entity);
353
381
  }
382
+ delete importNotifications[name];
383
+ }
354
384
  };
355
385
 
356
386
 
357
- /// support functions ////////////////////////////////////////////////////////////
387
+ // / support functions ////////////////////////////////////////////////////////////
358
388
 
359
389
  // compute dependency graph for entity that shows
360
390
  // (1) all (non-cyclic) associations,
361
391
  // (2) all cyclic associations, and
362
392
  // (3) the projection for $query() that covers all non-recursive associations
363
- exports.computeRelations = function (entity) {
364
- var entityName = entity.$_metadata.entityName;
365
- var projection = {}, assocs = [], cycles = [];
366
-
367
- var _build = function (entity, prefix, seen) {
368
- utils.setPropPath(projection, prefix + "$all", true);
369
- utils.forStruct(entity.$_mapping, {
370
- $association: function (m, f, p) {
371
- var target = m[f].$association.$entity;
372
- var targetEntity = m[f].$association.$class;
373
- if (typeof targetEntity === "undefined")
374
- throw new Error("*** ASSERT FAIL *** missing target entity");
375
- var isToMany = utils.isToMany(m[f].$association);
376
- var isLazy = m[f].$association.$lazy;
377
- if (seen.indexOf(target) >= 0) {
378
- cycles.push({field: prefix + p + f, assoc: m[f].$association});
379
- if (isToMany) {
380
- // force projection, but without recursion
381
- utils.setPropPath(projection, prefix + p + f, {$all: true});
382
- }
383
- } else {
384
- assocs.push({
385
- field: prefix + p + f, target: targetEntity, toMany: isToMany, lazy: isLazy
386
- });
387
- if (!isLazy)
388
- _build(targetEntity, prefix + p + f + ".", seen.concat([target]));
389
- }
390
- }
391
- });
392
- }
393
- _build(entity, "", [entityName]);
393
+ exports.computeRelations = function(entity) {
394
+ const entityName = entity.$_metadata.entityName;
395
+ const projection = {}; const assocs = []; const cycles = [];
396
+
397
+ const _build = function(entity, prefix, seen) {
398
+ utils.setPropPath(projection, `${prefix }$all`, true);
399
+ utils.forStruct(entity.$_mapping, {
400
+ $association: function(m, f, p) {
401
+ const target = m[f].$association.$entity;
402
+ const targetEntity = m[f].$association.$class;
403
+ if (typeof targetEntity === 'undefined') {
404
+ throw new Error('*** ASSERT FAIL *** missing target entity');
405
+ }
406
+ const isToMany = utils.isToMany(m[f].$association);
407
+ const isLazy = m[f].$association.$lazy;
408
+ if (seen.indexOf(target) >= 0) {
409
+ cycles.push({field: prefix + p + f, assoc: m[f].$association});
410
+ if (isToMany) {
411
+ // force projection, but without recursion
412
+ utils.setPropPath(projection, prefix + p + f, {$all: true});
413
+ }
414
+ } else {
415
+ assocs.push({
416
+ field: prefix + p + f, target: targetEntity, toMany: isToMany, lazy: isLazy,
417
+ });
418
+ if (!isLazy) {
419
+ _build(targetEntity, `${prefix + p + f }.`, seen.concat([target]));
420
+ }
421
+ }
422
+ },
423
+ });
424
+ };
425
+ _build(entity, '', [entityName]);
394
426
 
395
- logger.debug("node-cds import: analyzed " + entityName + ": proj = " +
396
- JSON.stringify(projection) + ", " + assocs.length + " assocs" + cycles.length + " cycles");
427
+ logger.debug(`node-cds import: analyzed ${ entityName }: proj = ${
428
+ JSON.stringify(projection) }, ${ assocs.length } assocs${ cycles.length } cycles`);
397
429
 
398
- return {
399
- projection: projection,
400
- associations: assocs,
401
- cycles: cycles
402
- };
403
- }
430
+ return {
431
+ projection: projection,
432
+ associations: assocs,
433
+ cycles: cycles,
434
+ };
435
+ };
404
436
 
405
437
 
406
438
  // (async) retrieve SQL metadata for database table
407
439
  function getSqlMetadata(tableName, schemaName, callback) {
408
- var sqlname = schemaName ? schemaName + "." + tableName : tableName;
409
- if (sqlname in sqlMetadata) {
410
- return callback(null, sqlMetadata[sqlname]);
440
+ const sqlname = schemaName ? `${schemaName }.${ tableName}` : tableName;
441
+ if (sqlname in sqlMetadata) {
442
+ return callback(null, sqlMetadata[sqlname]);
443
+ }
444
+
445
+ // get type information
446
+ const metadata = {};
447
+ const processResult = function(err, rows) {
448
+ if (err) {
449
+ return callback(err);
411
450
  }
412
-
413
- // get type information
414
- var metadata = {};
415
- var processResult = function(err, rows) {
416
- if (err)
417
- return callback(err);
418
- if (rows.length == 0)
419
- return callback("database table " + tableName + " not found");
420
- for (var i = 0; i < rows.length; i++) {
421
- var columnName = rows[i].COLUMN_NAME;
422
- metadata[columnName] = {
423
- $type: rows[i].DATA_TYPE_ID,
424
- $csType: rows[i].CS_DATA_TYPE_ID,
425
- $size: rows[i].SCALE,
426
- $key: rows[i].IS_PRIMARY_KEY === "TRUE"
427
- };
428
- }
429
- sqlMetadata[sqlname] = metadata;
430
- return callback(null, metadata);
431
- };
432
-
433
- transaction.getClient(null, function(err, client) {
434
- if (err)
435
- return callback(err);
436
- var schema = schemaName ? "'" + schemaName + "'" : "CURRENT_SCHEMA";
437
- client.exec(
438
- "SELECT tc.COLUMN_NAME, tc.DATA_TYPE_ID, tc.CS_DATA_TYPE_ID, tc.SCALE, cs.IS_PRIMARY_KEY " +
439
- "FROM SYS.TABLE_COLUMNS tc LEFT OUTER JOIN SYS.CONSTRAINTS cs " +
440
- "ON tc.SCHEMA_NAME = cs.SCHEMA_NAME AND tc.TABLE_NAME = cs.TABLE_NAME " +
441
- "AND tc.COLUMN_NAME = cs.COLUMN_NAME " +
442
- "WHERE tc.SCHEMA_NAME = " + schema + " AND tc.TABLE_NAME = '" + tableName + "'",
443
- function(err, result) {
444
- transaction.releaseClient(client);
445
- processResult(err, result);
446
- });
447
- });
451
+ if (rows.length == 0) {
452
+ return callback(`database table ${ tableName } not found`);
453
+ }
454
+ for (let i = 0; i < rows.length; i++) {
455
+ const columnName = rows[i].COLUMN_NAME;
456
+ metadata[columnName] = {
457
+ $type: rows[i].DATA_TYPE_ID,
458
+ $csType: rows[i].CS_DATA_TYPE_ID,
459
+ $size: rows[i].SCALE,
460
+ $key: rows[i].IS_PRIMARY_KEY === 'TRUE',
461
+ };
462
+ }
463
+ sqlMetadata[sqlname] = metadata;
464
+ return callback(null, metadata);
465
+ };
466
+
467
+ transaction.getClient(null, function(err, client) {
468
+ if (err) {
469
+ return callback(err);
470
+ }
471
+ const schema = schemaName ? `'${ schemaName }'` : 'CURRENT_SCHEMA';
472
+ client.exec(
473
+ // eslint-disable-next-line max-len
474
+ `${'SELECT tc.COLUMN_NAME, tc.DATA_TYPE_ID, tc.CS_DATA_TYPE_ID, tc.SCALE, cs.IS_PRIMARY_KEY ' +
475
+ 'FROM SYS.TABLE_COLUMNS tc LEFT OUTER JOIN SYS.CONSTRAINTS cs ' +
476
+ 'ON tc.SCHEMA_NAME = cs.SCHEMA_NAME AND tc.TABLE_NAME = cs.TABLE_NAME ' +
477
+ 'AND tc.COLUMN_NAME = cs.COLUMN_NAME ' +
478
+ 'WHERE tc.SCHEMA_NAME = '}${ schema } AND tc.TABLE_NAME = '${ tableName }'`,
479
+ function(err, result) {
480
+ transaction.releaseClient(client);
481
+ processResult(err, result);
482
+ });
483
+ });
448
484
  };
449
485
 
450
486
  // (async) retrieve CDS metadata from unofficial CDS metadata tables
451
487
  function getCdsMetadata(fullname, schema, options, callback) {
452
- if (options.$noCds)
453
- return callback(null, null);
454
- if (fullname in cdsMetadata)
455
- return callback(null, cdsMetadata[fullname]);
456
-
457
- // prepare queries for retrieving assocs and structs
458
- var sqlA =
488
+ if (options.$noCds) {
489
+ return callback(null, null);
490
+ }
491
+ if (fullname in cdsMetadata) {
492
+ return callback(null, cdsMetadata[fullname]);
493
+ }
494
+
495
+ // prepare queries for retrieving assocs and structs
496
+ const sqlA =
459
497
  'SELECT e.ARTIFACT_NAME AS "n",' +
460
498
  ' a.SCHEMA_NAME, e.SCHEMA_NAME,' +
461
499
  ' a.TARGET_ARTIFACT_SCHEMA_NAME AS "ts",' +
462
500
  ' a.TARGET_ARTIFACT_NAME AS "tn",' +
463
- ' a.JOIN_CONDITION AS "on",' + // unmanaged assoc
464
- ' e.ELEMENT_NAME AS "cn",' + // component name
465
- ' e.AUX_ELEMENT_INFO AS "fk"' + // alias name
501
+ ' a.JOIN_CONDITION AS "on",' + // unmanaged assoc
502
+ ' e.ELEMENT_NAME AS "cn",' + // component name
503
+ ' e.AUX_ELEMENT_INFO AS "fk"' + // alias name
466
504
  ' FROM SYS.CDS_ARTIFACT_DEFINITION(?, ?) AS e JOIN SYS.CDS_ASSOCIATIONS AS a' +
467
505
  ' ON e.ARTIFACT_NAME = a.ASSOCIATION_NAME AND a.SCHEMA_NAME = e.SCHEMA_NAME' +
468
506
  ' WHERE e.ARTIFACT_KIND = \'ASSOCIATION_ELEMENT\' OR' +
469
507
  ' (e.ARTIFACT_KIND = \'ASSOCIATION\' AND a.ASSOCIATION_KIND = \'UNMANAGED\')';
470
- var sqlACS =
508
+ const sqlACS =
471
509
  'SELECT e.ARTIFACT_NAME AS "n",' +
472
510
  ' a.SCHEMA_NAME, e.SCHEMA_NAME,' +
473
511
  ' a.TARGET_ARTIFACT_SCHEMA_NAME AS "ts",' +
474
512
  ' a.TARGET_ARTIFACT_NAME AS "tn",' +
475
- ' a.JOIN_CONDITION AS "on",' + // unmanaged assoc
476
- ' e.ELEMENT_NAME AS "cn",' + // component name
477
- ' e.AUX_ELEMENT_INFO AS "fk"' + // alias name
513
+ ' a.JOIN_CONDITION AS "on",' + // unmanaged assoc
514
+ ' e.ELEMENT_NAME AS "cn",' + // component name
515
+ ' e.AUX_ELEMENT_INFO AS "fk"' + // alias name
516
+ // eslint-disable-next-line max-len
478
517
  ' FROM SYS.CDS_ARTIFACT_DEFINITION(CURRENT_SCHEMA, ?) AS e JOIN SYS.CDS_ASSOCIATIONS AS a' +
479
518
  ' ON e.ARTIFACT_NAME = a.ASSOCIATION_NAME AND a.SCHEMA_NAME = e.SCHEMA_NAME' +
480
519
  ' WHERE e.ARTIFACT_KIND = \'ASSOCIATION_ELEMENT\' OR' +
481
520
  ' (e.ARTIFACT_KIND = \'ASSOCIATION\' AND a.ASSOCIATION_KIND = \'UNMANAGED\')';
482
- var sqlS =
521
+ const sqlS =
483
522
  'SELECT ELEMENT_NAME AS "n",' +
484
523
  ' USED_ARTIFACT_SCHEMA AS "ts",' +
485
524
  ' USED_ARTIFACT_NAME AS "tn"' +
486
525
  ' FROM SYS.CDS_ARTIFACT_DEFINITION(?, ?)' +
487
526
  ' WHERE USED_ARTIFACT_KIND = \'STRUCTURED_TYPE\'';
488
- var sqlSCS =
527
+ const sqlSCS =
489
528
  'SELECT ELEMENT_NAME AS "n",' +
490
529
  ' USED_ARTIFACT_SCHEMA AS "ts",' +
491
530
  ' USED_ARTIFACT_NAME AS "tn"' +
492
531
  ' FROM SYS.CDS_ARTIFACT_DEFINITION(CURRENT_SCHEMA, ?)' +
493
532
  ' WHERE USED_ARTIFACT_KIND = \'STRUCTURED_TYPE\'';
494
533
 
495
- transaction.getClient(null, function(err, client) {
496
- if (err)
497
- return callback(err);
498
- async.series([
499
- function(cb) { client.prepare(sqlA, cb); },
500
- function(cb) { client.prepare(sqlACS, cb); },
501
- function(cb) { client.prepare(sqlS, cb); },
502
- function(cb) { client.prepare(sqlSCS, cb); }
503
- ], function(err, stmts) {
504
- if (err)
505
- throw new Error("*** ASSERT FAIL *** invalid CDS metadata query");
506
- getStructsAndAssocs(stmts, schema, fullname, function (err, data) {
507
- transaction.releaseClient(client);
508
- cdsMetadata[fullname] = data;
509
- return callback(err, data);
510
- });
511
- });
534
+ transaction.getClient(null, function(err, client) {
535
+ if (err) {
536
+ return callback(err);
537
+ }
538
+ async.series([
539
+ function(cb) {
540
+ client.prepare(sqlA, cb);
541
+ },
542
+ function(cb) {
543
+ client.prepare(sqlACS, cb);
544
+ },
545
+ function(cb) {
546
+ client.prepare(sqlS, cb);
547
+ },
548
+ function(cb) {
549
+ client.prepare(sqlSCS, cb);
550
+ },
551
+ ], function(err, stmts) {
552
+ if (err) {
553
+ throw new Error('*** ASSERT FAIL *** invalid CDS metadata query');
554
+ }
555
+ getStructsAndAssocs(stmts, schema, fullname, function(err, data) {
556
+ transaction.releaseClient(client);
557
+ cdsMetadata[fullname] = data;
558
+ return callback(err, data);
559
+ });
512
560
  });
561
+ });
513
562
  }
514
563
 
515
564
  // async
516
565
  // recursively retrieve assoc and struct information
517
566
  function getStructsAndAssocs(stmts, schema, fullname, callback) {
518
- logger.debug("node-cds import: retrieve CDS assoc for " + fullname);
519
-
520
- // retrieve association metadata from CDS tables
521
- var getAssocs = function(callback) {
522
- var s = schema ? stmts[0] : stmts[1];
523
- var a = schema ? [schema, fullname] : [fullname];
524
- s.exec(a, function(err, rows) {
525
- var assocs = {};
526
- if (err)
527
- return callback("Error retrieving CDS association metadata: " + err, assocs);
528
- for (var i = 0; i < rows.length; ++i) {
529
- var fieldName = rows[i].n.slice(fullname.length + 1);
530
- if (!(fieldName in assocs)) {
531
- assocs[fieldName] = {
532
- $association: rows[i].tn,
533
- $schema: rows[i].ts
534
- };
535
- }
536
- if (rows[i].on && rows[i].on.length) {
537
- var cond = String.fromCharCode.apply(null, rows[i].on);
538
- assocs[fieldName].$on = cond;
539
- } else {
540
- var foreignKey = rows[i].cn;
541
- var foreignKeyAlias = rows[i].fk;
542
- if (foreignKey !== foreignKeyAlias && foreignKeyAlias !== null) {
543
- if (!("$aliases" in assocs[fieldName]))
544
- assocs[fieldName].$aliases = {};
545
- assocs[fieldName].$aliases[foreignKey] = foreignKeyAlias;
546
- }
547
- }
548
- }
549
- return callback(null, assocs);
550
- });
551
- };
552
-
553
- // retrieve structure metadata from CDS tables
554
- var getStructs = function(callback) {
555
- var s = schema ? stmts[2] : stmts[3];
556
- var a = schema ? [schema, fullname] : [fullname];
557
- s.exec(a, function(err, rows) {
558
- var structs = {};
559
- if (err)
560
- return callback("Error retrieving CDS type metadata: " + err, structs);
561
- for (var i = 0; i < rows.length; ++i) {
562
- var componentName = rows[i].n;
563
- structs[componentName] = {
564
- schema: rows[i].ts,
565
- name: rows[i].tn
566
- };
567
- }
568
- return callback(null, structs);
569
- });
570
- };
571
-
572
- // recursively get assocs and structs
573
- getAssocs(function (err, assocs) {
574
- if (err)
575
- return callback(err, null);
576
- getStructs(function (err, structs) {
577
- if (err)
578
- return callback(err, null);
579
- var fns = [];
580
- for (var s in structs) {
581
- var fn = (function (result, field) {
582
- return function (cb) {
583
- getStructsAndAssocs(stmts, structs[field].schema, structs[field].name,
584
- function (err, data) {
585
- if (err)
586
- return cb(err, null);
587
- result[field] = data;
588
- cb(null); // result is ignored
589
- });
590
- };
591
- })(assocs, s);
592
- fns.push(fn);
567
+ logger.debug(`node-cds import: retrieve CDS assoc for ${ fullname}`);
568
+
569
+ // retrieve association metadata from CDS tables
570
+ const getAssocs = function(callback) {
571
+ const s = schema ? stmts[0] : stmts[1];
572
+ const a = schema ? [schema, fullname] : [fullname];
573
+ s.exec(a, function(err, rows) {
574
+ const assocs = {};
575
+ if (err) {
576
+ return callback(`Error retrieving CDS association metadata: ${ err}`, assocs);
577
+ }
578
+ for (let i = 0; i < rows.length; ++i) {
579
+ const fieldName = rows[i].n.slice(fullname.length + 1);
580
+ if (!(fieldName in assocs)) {
581
+ assocs[fieldName] = {
582
+ $association: rows[i].tn,
583
+ $schema: rows[i].ts,
584
+ };
585
+ }
586
+ if (rows[i].on && rows[i].on.length) {
587
+ const cond = String.fromCharCode.apply(null, rows[i].on);
588
+ assocs[fieldName].$on = cond;
589
+ } else {
590
+ const foreignKey = rows[i].cn;
591
+ const foreignKeyAlias = rows[i].fk;
592
+ if (foreignKey !== foreignKeyAlias && foreignKeyAlias !== null) {
593
+ if (!('$aliases' in assocs[fieldName])) {
594
+ assocs[fieldName].$aliases = {};
593
595
  }
594
- async.parallel(fns, function (err, ignored) {
595
- callback(err, assocs);
596
- });
597
- });
596
+ assocs[fieldName].$aliases[foreignKey] = foreignKeyAlias;
597
+ }
598
+ }
599
+ }
600
+ return callback(null, assocs);
601
+ });
602
+ };
603
+
604
+ // retrieve structure metadata from CDS tables
605
+ const getStructs = function(callback) {
606
+ const s = schema ? stmts[2] : stmts[3];
607
+ const a = schema ? [schema, fullname] : [fullname];
608
+ s.exec(a, function(err, rows) {
609
+ const structs = {};
610
+ if (err) {
611
+ return callback(`Error retrieving CDS type metadata: ${ err}`, structs);
612
+ }
613
+ for (let i = 0; i < rows.length; ++i) {
614
+ const componentName = rows[i].n;
615
+ structs[componentName] = {
616
+ schema: rows[i].ts,
617
+ name: rows[i].tn,
618
+ };
619
+ }
620
+ return callback(null, structs);
621
+ });
622
+ };
623
+
624
+ // recursively get assocs and structs
625
+ getAssocs(function(err, assocs) {
626
+ if (err) {
627
+ return callback(err, null);
628
+ }
629
+ getStructs(function(err, structs) {
630
+ if (err) {
631
+ return callback(err, null);
632
+ }
633
+ const fns = [];
634
+ for (const s in structs) {
635
+ const fn = (function(result, field) {
636
+ return function(cb) {
637
+ getStructsAndAssocs(stmts, structs[field].schema, structs[field].name,
638
+ function(err, data) {
639
+ if (err) {
640
+ return cb(err, null);
641
+ }
642
+ result[field] = data;
643
+ cb(null); // result is ignored
644
+ });
645
+ };
646
+ })(assocs, s);
647
+ fns.push(fn);
648
+ }
649
+ async.parallel(fns, function(err, ignored) {
650
+ callback(err, assocs);
651
+ });
598
652
  });
653
+ });
599
654
  }
600
655
 
601
656
  function buildSqlMapping(sqlMetadata, ignoredColumns) {
602
- ignoredColumns = ignoredColumns || [];
603
- // add all SQL columns as fields
604
- var mapping = {};
605
- for (var columnName in sqlMetadata) {
606
- if (ignoredColumns.indexOf(columnName) >= 0)
607
- continue;
608
- var field = utils.mkPropPath(mapping, columnName);
609
- field.$column = columnName;
610
- if ("$key" in sqlMetadata[columnName])
611
- field.$key = sqlMetadata[columnName].$key;
657
+ ignoredColumns = ignoredColumns || [];
658
+ // add all SQL columns as fields
659
+ const mapping = {};
660
+ for (const columnName in sqlMetadata) {
661
+ if (ignoredColumns.indexOf(columnName) >= 0) {
662
+ continue;
663
+ }
664
+ const field = utils.mkPropPath(mapping, columnName);
665
+ field.$column = columnName;
666
+ if ('$key' in sqlMetadata[columnName]) {
667
+ field.$key = sqlMetadata[columnName].$key;
612
668
  }
613
- return mapping;
669
+ }
670
+ return mapping;
614
671
  }
615
672
 
616
673
  function buildCdsMapping(cdsMetadata) {
617
- // convert CDS association metadata into mapping format
618
- var assocs = {};
619
- utils.forStruct(cdsMetadata, {
620
- $association: function(s, f, p) {
621
- var a = utils.mkPropPath(assocs, p + f);
622
- a.$association = {
623
- $entity: s[f].$association,
624
- $schema: s[f].$schema,
625
- $lazy: false
626
- };
627
- if (s[f].$on)
628
- a.$association.$on = s[f].$on;
629
- if (s[f].$aliases)
630
- a.$aliases = s[f].$aliases;
631
- }
632
- });
633
- return assocs;
674
+ // convert CDS association metadata into mapping format
675
+ const assocs = {};
676
+ utils.forStruct(cdsMetadata, {
677
+ $association: function(s, f, p) {
678
+ const a = utils.mkPropPath(assocs, p + f);
679
+ a.$association = {
680
+ $entity: s[f].$association,
681
+ $schema: s[f].$schema,
682
+ $lazy: false,
683
+ };
684
+ if (s[f].$on) {
685
+ a.$association.$on = s[f].$on;
686
+ }
687
+ if (s[f].$aliases) {
688
+ a.$aliases = s[f].$aliases;
689
+ }
690
+ },
691
+ });
692
+ return assocs;
634
693
  }
635
694
 
636
695
  function mergeMapping(mapping, newFields) {
637
- // recursively copy nested properties from src to dst
638
- var copy = function(dst, src) {
639
- // special handling for $association/$column overrides
640
- if ("$column" in src && "$association" in dst ||
641
- "$association" in src && "$column" in dst) {
642
- for (var p in dst)
643
- delete dst[p];
644
- copy(dst, src); // redo
645
- return;
646
- }
647
- // copy properties
648
- for (var f in src) {
649
- if (typeof src[f] === "object") {
650
- // merge props of src structure
651
- if (!(f in dst))
652
- dst[f] = {};
653
- copy(dst[f], src[f]);
654
- } else if (src[f] === false) {
655
- // remove prop in dst
656
- delete dst[f];
657
- } else {
658
- // copy prop from src to dst
659
- dst[f] = src[f];
660
- }
661
- }
662
- };
663
-
664
- //var result = Entities.cloneJSON(mapping);
665
- copy(mapping, newFields);
666
-
667
- // aliases: rename fields by moving
668
- utils.forStruct(mapping, {
669
- $aliases: function (s, f, p) {
670
- var aliases = s[f].$aliases;
671
- for (var oldf in aliases) {
672
- var newf = aliases[oldf];
673
- utils.setPropPath(s[f], newf, s[f][oldf]);
674
- delete s[f][oldf];
675
- }
676
- delete s[f].$aliases;
696
+ // recursively copy nested properties from src to dst
697
+ const copy = function(dst, src) {
698
+ // special handling for $association/$column overrides
699
+ if ('$column' in src && '$association' in dst ||
700
+ '$association' in src && '$column' in dst) {
701
+ for (const p in dst) {
702
+ delete dst[p];
703
+ }
704
+ copy(dst, src); // redo
705
+ return;
706
+ }
707
+ // copy properties
708
+ for (const f in src) {
709
+ if (typeof src[f] === 'object') {
710
+ // merge props of src structure
711
+ if (!(f in dst)) {
712
+ dst[f] = {};
677
713
  }
678
- });
679
-
680
- return mapping;
714
+ copy(dst[f], src[f]);
715
+ } else if (src[f] === false) {
716
+ // remove prop in dst
717
+ delete dst[f];
718
+ } else {
719
+ // copy prop from src to dst
720
+ dst[f] = src[f];
721
+ }
722
+ }
723
+ };
724
+
725
+ // var result = Entities.cloneJSON(mapping);
726
+ copy(mapping, newFields);
727
+
728
+ // aliases: rename fields by moving
729
+ utils.forStruct(mapping, {
730
+ $aliases: function(s, f, p) {
731
+ const aliases = s[f].$aliases;
732
+ for (const oldf in aliases) {
733
+ const newf = aliases[oldf];
734
+ utils.setPropPath(s[f], newf, s[f][oldf]);
735
+ delete s[f][oldf];
736
+ }
737
+ delete s[f].$aliases;
738
+ },
739
+ });
740
+
741
+ return mapping;
681
742
  }
682
743
 
683
744
  // various validity and consistency checks for mapping
684
745
  function checkMapping(mapping) {
685
- var error = null;
686
- utils.forStruct(mapping, {
687
- $association: function (s, f, p) {
688
- var assoc = s[f].$association;
689
- if ("$on" in assoc && "$cascadeDiscard" in assoc)
690
- error = "Cascade discard invalid for unmanaged associations";
691
- }
692
- });
693
- return error;
746
+ let error = null;
747
+ utils.forStruct(mapping, {
748
+ $association: function(s, f, p) {
749
+ const assoc = s[f].$association;
750
+ if ('$on' in assoc && '$cascadeDiscard' in assoc) {
751
+ error = 'Cascade discard invalid for unmanaged associations';
752
+ }
753
+ },
754
+ });
755
+ return error;
694
756
  }
695
757
 
696
758
 
697
759
  // testing and debugging
698
760
 
699
761
  exports._clearImports = function() {
700
- logger.info("node-cds: reset imports");
701
- knownEntities = {};
762
+ logger.info('node-cds: reset imports');
763
+ knownEntities = {};
702
764
  };