@sap/cds 5.4.6 → 5.5.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 (228) hide show
  1. package/CHANGELOG.md +208 -2
  2. package/apis/ql.d.ts +17 -15
  3. package/app/index.js +1 -1
  4. package/bin/build/buildTaskEngine.js +26 -42
  5. package/bin/build/buildTaskFactory.js +6 -10
  6. package/bin/build/buildTaskHandler.js +2 -4
  7. package/bin/build/buildTaskProvider.js +3 -1
  8. package/bin/build/buildTaskProviderFactory.js +9 -15
  9. package/bin/build/constants.js +15 -3
  10. package/bin/build/index.js +5 -4
  11. package/bin/build/mtaUtil.js +8 -11
  12. package/bin/build/provider/buildTaskHandlerEdmx.js +63 -6
  13. package/bin/build/provider/buildTaskHandlerInternal.js +2 -34
  14. package/bin/build/provider/buildTaskProviderInternal.js +16 -42
  15. package/bin/build/provider/fiori/index.js +13 -24
  16. package/bin/build/provider/hana/2migration.js +17 -15
  17. package/bin/build/provider/hana/2tabledata.js +52 -48
  18. package/bin/build/provider/hana/index.js +27 -25
  19. package/bin/build/provider/hana/migrationtable.js +91 -67
  20. package/bin/build/provider/java-cf/index.js +14 -24
  21. package/bin/build/provider/mtx/index.js +12 -14
  22. package/bin/build/provider/node-cf/index.js +18 -32
  23. package/bin/cds.js +5 -5
  24. package/bin/serve.js +29 -23
  25. package/bin/version.js +0 -1
  26. package/lib/compile/etc/_localized.js +4 -9
  27. package/lib/compile/for/sql.js +5 -2
  28. package/lib/compile/parse.js +25 -17
  29. package/lib/compile/to/srvinfo.js +2 -1
  30. package/lib/connect/bindings.js +2 -1
  31. package/lib/connect/index.js +48 -49
  32. package/lib/core/classes.js +1 -1
  33. package/lib/core/reflect.js +10 -2
  34. package/lib/deploy.js +26 -23
  35. package/lib/env/defaults.js +13 -6
  36. package/lib/env/index.js +73 -78
  37. package/lib/env/requires.js +38 -19
  38. package/lib/index.js +9 -10
  39. package/lib/lazy.js +2 -2
  40. package/lib/log/index.js +33 -45
  41. package/lib/log/service/index.js +2 -2
  42. package/lib/ql/CREATE.js +14 -9
  43. package/lib/ql/DELETE.js +6 -5
  44. package/lib/ql/DROP.js +12 -9
  45. package/lib/ql/INSERT.js +40 -16
  46. package/lib/ql/Query.js +67 -40
  47. package/lib/ql/SELECT.js +162 -127
  48. package/lib/ql/UPDATE.js +74 -42
  49. package/lib/ql/Whereable.js +77 -87
  50. package/lib/ql/index.js +36 -24
  51. package/lib/ql/parse.js +35 -0
  52. package/lib/req/context.js +44 -8
  53. package/lib/req/locale.js +7 -7
  54. package/lib/serve/Service-api.js +21 -14
  55. package/lib/serve/Service-dispatch.js +28 -12
  56. package/lib/serve/Transaction.js +22 -10
  57. package/lib/serve/index.js +16 -11
  58. package/lib/utils/axios.js +23 -16
  59. package/lib/utils/data.js +35 -0
  60. package/lib/utils/tests.js +27 -18
  61. package/libx/_runtime/audit/generic/personal/access.js +81 -0
  62. package/libx/_runtime/audit/generic/personal/constants.js +4 -0
  63. package/libx/_runtime/audit/generic/personal/index.js +50 -0
  64. package/libx/_runtime/audit/generic/personal/modification.js +138 -0
  65. package/libx/_runtime/audit/generic/personal/utils.js +186 -0
  66. package/libx/_runtime/audit/utils/v2.js +10 -4
  67. package/libx/_runtime/cds-services/adapter/odata-v4/Dispatcher.js +5 -5
  68. package/libx/_runtime/cds-services/adapter/odata-v4/OData.js +6 -7
  69. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/action.js +5 -7
  70. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/create.js +5 -7
  71. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/delete.js +2 -3
  72. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/error.js +4 -0
  73. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/metadata.js +7 -4
  74. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/read.js +59 -8
  75. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/request.js +11 -1
  76. package/libx/_runtime/cds-services/adapter/odata-v4/handlers/update.js +6 -10
  77. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/ExpressionToCQN.js +3 -46
  78. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/applyToCQN.js +2 -5
  79. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/createToCQN.js +2 -2
  80. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/deleteToCQN.js +4 -3
  81. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/expandToCQN.js +1 -2
  82. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/index.js +0 -1
  83. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/selectHelper.js +1 -1
  84. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/updateToCQN.js +2 -2
  85. package/libx/_runtime/cds-services/adapter/odata-v4/odata-to-cqn/utils.js +16 -18
  86. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/edm/EdmEntityType.js +6 -3
  87. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-commons/format/RepresentationKind.js +4 -1
  88. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/core/OdataRequest.js +1 -0
  89. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/serializer/SerializerFactory.js +15 -2
  90. package/libx/_runtime/cds-services/adapter/odata-v4/okra/odata-server/validator/OperationValidator.js +1 -0
  91. package/libx/_runtime/cds-services/adapter/odata-v4/to.js +1 -1
  92. package/libx/_runtime/cds-services/adapter/odata-v4/utils/data.js +8 -1
  93. package/libx/_runtime/cds-services/adapter/odata-v4/utils/handlerUtils.js +6 -1
  94. package/libx/_runtime/cds-services/adapter/odata-v4/utils/omitValues.js +12 -5
  95. package/libx/_runtime/cds-services/adapter/odata-v4/utils/readAfterWrite.js +1 -7
  96. package/libx/_runtime/cds-services/adapter/odata-v4/utils/request.js +7 -7
  97. package/libx/_runtime/cds-services/adapter/odata-v4/utils/result.js +14 -18
  98. package/libx/_runtime/cds-services/adapter/odata-v4/utils/stream.js +13 -13
  99. package/libx/_runtime/cds-services/adapter/rest/handlers/create.js +0 -1
  100. package/libx/_runtime/cds-services/adapter/rest/handlers/operation.js +2 -1
  101. package/libx/_runtime/cds-services/adapter/rest/handlers/read.js +2 -2
  102. package/libx/_runtime/cds-services/adapter/rest/rest-to-cqn/index.js +2 -4
  103. package/libx/_runtime/cds-services/adapter/rest/utils/result.js +4 -2
  104. package/libx/_runtime/cds-services/services/Service.js +40 -5
  105. package/libx/_runtime/cds-services/services/utils/columns.js +13 -7
  106. package/libx/_runtime/cds-services/services/utils/compareJson.js +88 -4
  107. package/libx/_runtime/cds-services/services/utils/differ.js +24 -6
  108. package/libx/_runtime/cds-services/services/utils/handlerUtils.js +2 -2
  109. package/libx/_runtime/common/composition/data.js +44 -55
  110. package/libx/_runtime/common/composition/delete.js +97 -71
  111. package/libx/_runtime/common/composition/index.js +2 -1
  112. package/libx/_runtime/common/composition/insert.js +34 -11
  113. package/libx/_runtime/common/composition/tree.js +119 -92
  114. package/libx/_runtime/common/composition/update.js +4 -1
  115. package/libx/_runtime/common/composition/utils.js +1 -3
  116. package/libx/_runtime/common/constants/draft.js +12 -1
  117. package/libx/_runtime/common/generic/auth.js +6 -22
  118. package/libx/_runtime/common/generic/crud.js +14 -13
  119. package/libx/_runtime/common/generic/input.js +23 -26
  120. package/libx/_runtime/common/generic/put.js +1 -1
  121. package/libx/_runtime/common/generic/sorting.js +16 -16
  122. package/libx/_runtime/common/i18n/index.js +1 -1
  123. package/libx/_runtime/common/i18n/messages.properties +4 -0
  124. package/libx/_runtime/common/utils/backlinks.js +12 -5
  125. package/libx/_runtime/common/utils/cqn.js +6 -1
  126. package/libx/_runtime/common/utils/cqn2cqn4sql.js +102 -101
  127. package/libx/_runtime/common/utils/csn.js +47 -4
  128. package/libx/_runtime/common/utils/data.js +0 -37
  129. package/libx/_runtime/common/utils/enrichWithKeysFromWhere.js +1 -1
  130. package/libx/_runtime/common/utils/entityFromCqn.js +7 -24
  131. package/libx/_runtime/common/utils/foreignKeyPropagations.js +39 -7
  132. package/libx/_runtime/common/utils/generateOnCond.js +11 -12
  133. package/libx/_runtime/common/utils/onlyKeysRemain.js +10 -0
  134. package/libx/_runtime/common/utils/path.js +35 -0
  135. package/libx/_runtime/common/utils/postProcessing.js +86 -0
  136. package/libx/_runtime/common/utils/quotingStyles.js +37 -26
  137. package/libx/_runtime/common/utils/resolveView.js +223 -171
  138. package/libx/_runtime/common/utils/rewriteAsterisk.js +46 -26
  139. package/libx/_runtime/common/utils/structured.js +6 -12
  140. package/libx/_runtime/common/utils/template.js +10 -5
  141. package/libx/_runtime/common/utils/templateDelimiter.js +1 -0
  142. package/libx/_runtime/common/utils/templateProcessor.js +22 -30
  143. package/libx/_runtime/common/utils/union.js +31 -0
  144. package/libx/_runtime/common/utils/unionCqnTemplate.js +184 -0
  145. package/libx/_runtime/db/Service.js +1 -1
  146. package/libx/_runtime/db/data-conversion/timestamp.js +2 -9
  147. package/libx/_runtime/db/expand/expandCQNToJoin.js +204 -297
  148. package/libx/_runtime/db/expand/index.js +3 -3
  149. package/libx/_runtime/db/expand/rawToExpanded.js +36 -7
  150. package/libx/_runtime/db/generic/index.js +1 -1
  151. package/libx/_runtime/db/generic/input.js +5 -7
  152. package/libx/_runtime/db/generic/integrity.js +1 -1
  153. package/libx/_runtime/db/generic/rewrite.js +2 -10
  154. package/libx/_runtime/db/generic/update.js +13 -5
  155. package/libx/_runtime/db/generic/virtual.js +22 -58
  156. package/libx/_runtime/db/query/delete.js +7 -4
  157. package/libx/_runtime/db/query/insert.js +6 -4
  158. package/libx/_runtime/db/query/read.js +13 -20
  159. package/libx/_runtime/db/query/run.js +4 -1
  160. package/libx/_runtime/db/query/update.js +5 -4
  161. package/libx/_runtime/db/sql-builder/ExpressionBuilder.js +35 -2
  162. package/libx/_runtime/db/sql-builder/FunctionBuilder.js +17 -2
  163. package/libx/_runtime/db/sql-builder/InsertBuilder.js +6 -5
  164. package/libx/_runtime/db/sql-builder/ReferenceBuilder.js +10 -0
  165. package/libx/_runtime/db/sql-builder/SelectBuilder.js +35 -24
  166. package/libx/_runtime/db/sql-builder/UpdateBuilder.js +14 -4
  167. package/libx/_runtime/db/sql-builder/arrayed.js +4 -0
  168. package/libx/_runtime/db/utils/deep.js +8 -0
  169. package/libx/_runtime/db/utils/generateAliases.js +2 -1
  170. package/libx/_runtime/fiori/generic/activate.js +19 -15
  171. package/libx/_runtime/fiori/generic/before.js +3 -11
  172. package/libx/_runtime/fiori/generic/cancel.js +1 -1
  173. package/libx/_runtime/fiori/generic/delete.js +3 -1
  174. package/libx/_runtime/fiori/generic/edit.js +12 -2
  175. package/libx/_runtime/fiori/generic/new.js +5 -5
  176. package/libx/_runtime/fiori/generic/patch.js +0 -18
  177. package/libx/_runtime/fiori/generic/read.js +241 -189
  178. package/libx/_runtime/fiori/utils/delete.js +36 -7
  179. package/libx/_runtime/fiori/utils/handler.js +43 -44
  180. package/libx/_runtime/fiori/utils/where.js +30 -15
  181. package/libx/_runtime/hana/customBuilder/CustomSelectBuilder.js +4 -6
  182. package/libx/_runtime/hana/execute.js +2 -2
  183. package/libx/_runtime/hana/localized.js +4 -4
  184. package/libx/_runtime/hana/pool.js +29 -14
  185. package/libx/_runtime/hana/search2cqn4sql.js +2 -1
  186. package/libx/_runtime/hana/searchToContains.js +18 -14
  187. package/libx/_runtime/index.js +0 -5
  188. package/libx/_runtime/messaging/AMQPWebhookMessaging.js +13 -5
  189. package/libx/_runtime/messaging/common-utils/naming-conventions.js +4 -1
  190. package/libx/_runtime/messaging/enterprise-messaging-utils/EMManagement.js +31 -19
  191. package/libx/_runtime/messaging/enterprise-messaging-utils/registerEndpoints.js +1 -2
  192. package/libx/_runtime/messaging/enterprise-messaging.js +6 -4
  193. package/libx/_runtime/messaging/service.js +7 -6
  194. package/libx/_runtime/odata/cqn2odata.js +110 -43
  195. package/libx/_runtime/odata/index.js +26 -48
  196. package/libx/_runtime/odata/odata2cqn.js +1 -6154
  197. package/libx/_runtime/odata/odata2cqn.pegjs +559 -0
  198. package/libx/_runtime/odata/readToCqn.js +94 -64
  199. package/libx/_runtime/remote/Service.js +74 -21
  200. package/libx/_runtime/remote/cqn2odata/index.js +1 -5
  201. package/libx/_runtime/remote/utils/client.js +24 -101
  202. package/libx/_runtime/remote/utils/dataConversion.js +27 -12
  203. package/libx/_runtime/sqlite/Service.js +3 -5
  204. package/libx/_runtime/sqlite/execute.js +23 -24
  205. package/libx/_runtime/sqlite/localized.js +12 -7
  206. package/libx/_runtime/types/api.js +10 -0
  207. package/package.json +1 -1
  208. package/server.js +16 -2
  209. package/lib/ql/grammar.pegjs +0 -208
  210. package/lib/ql/parser.js +0 -1
  211. package/lib/ql/rt/DELETE.js +0 -29
  212. package/lib/ql/rt/INSERT.js +0 -23
  213. package/lib/ql/rt/Query.js +0 -84
  214. package/lib/ql/rt/SELECT.js +0 -174
  215. package/lib/ql/rt/UPDATE.js +0 -119
  216. package/lib/ql/rt/_helpers.js +0 -91
  217. package/lib/ql/rt/index.js +0 -32
  218. package/libx/_runtime/audit/generic/personal.js +0 -260
  219. package/libx/_runtime/cds-services/statements/BaseStatement.js +0 -72
  220. package/libx/_runtime/cds-services/statements/Create.js +0 -57
  221. package/libx/_runtime/cds-services/statements/Delete.js +0 -33
  222. package/libx/_runtime/cds-services/statements/Drop.js +0 -42
  223. package/libx/_runtime/cds-services/statements/Insert.js +0 -201
  224. package/libx/_runtime/cds-services/statements/Select.js +0 -826
  225. package/libx/_runtime/cds-services/statements/Update.js +0 -181
  226. package/libx/_runtime/cds-services/statements/Where.js +0 -726
  227. package/libx/_runtime/cds-services/statements/index.js +0 -25
  228. package/libx/_runtime/common/generic/resolve-mock.js +0 -9
@@ -1,42 +1,46 @@
1
- const DEBUG = /\b(y|all|tabledata)\b/.test (process.env.DEBUG) && console.warn // eslint-disable-line
2
- const _warn = (message) => { console.error ('[WARNING] [hdbtabledata] -', message) } // eslint-disable-line
3
- const cds = require ('../../cds')
1
+ const cds = require('../../cds')
4
2
  const CSV = require('../../csv-reader')
5
- const { getElementCdsPersistenceName, getArtifactCdsPersistenceName } = require ('@sap/cds-compiler')
3
+ const { getElementCdsPersistenceName, getArtifactCdsPersistenceName } = require('@sap/cds-compiler')
4
+ const { LOG_MODULE_NAMES } = require('../../constants')
6
5
  const { fs, path, isdir } = cds.utils
7
6
  const { readdir } = fs.promises
7
+ let logger = cds.log(LOG_MODULE_NAMES)
8
+
9
+ module.exports = async (model, options = {}) => {
10
+ if (options.logger) {
11
+ logger = options.logger
12
+ }
8
13
 
9
- module.exports = async (model, options={}) => {
10
14
  const baseDir = options.baseDir // where the hdbtabledata will be located, for usage in the file_name path
11
- const dirs = Array.isArray(options.dirs) ? options.dirs : _csvDirs (model.$sources.map (path.dirname))
12
- if (dirs.length === 0) return [] // nothing to do
15
+ const dirs = Array.isArray(options.dirs) ? options.dirs : _csvDirs(model.$sources.map(path.dirname))
16
+ if (dirs.length === 0) return [] // nothing to do
13
17
  const naming = options.sqlMapping || options.names || cds.env.sql.names
14
18
 
15
19
  const datas = (await Promise.all(dirs.map(async dir => {
16
20
  let files = []
17
21
  if (isdir(dir)) {
18
- files = await readdir (dir)
22
+ files = await readdir(dir)
19
23
  }
20
- return Promise.all(files.filter (_csvs).map (file => _tabledata4 (dir, file, cds.linked (model), baseDir, naming)))
24
+ return Promise.all(files.filter(_csvs).map(file => _tabledata4(dir, file, cds.linked(model), baseDir, naming)))
21
25
  })))
22
- .reduce((a, b) => a.concat(b), [])
23
- return _toOutput (datas)
26
+ .reduce((a, b) => a.concat(b), [])
27
+ return _toOutput(datas)
24
28
  }
25
29
 
26
- async function _tabledata4 (dir, csvFile, model, baseDir, naming) {
30
+ async function _tabledata4(dir, csvFile, model, baseDir, naming) {
27
31
  const baseFileName = path.parse(csvFile).name
28
- const entityName = baseFileName.replace(/-/g,'.')
29
- const entity = _entity4 (entityName, model)
32
+ const entityName = baseFileName.replace(/-/g, '.')
33
+ const entity = _entity4(entityName, model)
30
34
  if (!entity) {
31
35
  let message = `no entity '${entityName}' found for CSV file '${path.relative(process.cwd(), path.join(dir, csvFile))}'`
32
36
  const candidate = Object.keys(model.definitions)
33
37
  .filter(name => !name.startsWith('localized.'))
34
- .find (name => name.toLowerCase().includes(entityName.toLowerCase()))
35
- if (candidate) message += `. Did you mean '${candidate}'?`
36
- return _warn (message)
38
+ .find(name => name.toLowerCase().includes(entityName.toLowerCase()))
39
+ if (candidate) message += `. Did you mean '${candidate}'?`
40
+ return logger.warn(`[hdbtabledata] ${message}`)
37
41
  }
38
42
 
39
- const tableName = getArtifactCdsPersistenceName (entity.name, naming, model)
43
+ const tableName = getArtifactCdsPersistenceName(entity.name, naming, model)
40
44
  const tabledata = { format_version: 1, imports: [] }
41
45
  const _import = {
42
46
  target_table: tableName,
@@ -47,84 +51,84 @@ async function _tabledata4 (dir, csvFile, model, baseDir, naming) {
47
51
 
48
52
  const file = path.join(dir, csvFile)
49
53
  const reader = fs.createReadStream(file)
50
- const { cols, delimiter } = await CSV.readHeader (reader)
51
- if (cols.length === 0) return // no columns at all -> skip import
54
+ const { cols, delimiter } = await CSV.readHeader(reader)
55
+ if (cols.length === 0) return // no columns at all -> skip import
52
56
 
53
57
  cols.forEach(csvCol => {
54
58
  // Only translate the column name, but do not check for the existence of the element in the model.
55
59
  // This gets tricky for foreign key elements, and the DB deployment anyways checks the column.
56
- const tableCol = getElementCdsPersistenceName (csvCol, naming)
57
- _import.import_settings.import_columns.push (tableCol)
60
+ const tableCol = getElementCdsPersistenceName(csvCol, naming)
61
+ _import.import_settings.import_columns.push(tableCol)
58
62
  _import.column_mappings[tableCol] = csvCol
59
63
  })
60
64
 
61
65
  _import.source_data.type_config.delimiter = delimiter
62
66
 
63
67
  // add a locale filter for mono-lingual files that refer to generated text tables
64
- if (entity.elements.locale) {
65
- const locale = /_texts_(.+)\.csv$/.test (csvFile) ? RegExp.$1 : null
68
+ if (entity.elements.locale) {
69
+ const locale = /_texts_(.+)\.csv$/.test(csvFile) ? RegExp.$1 : null
66
70
  if (locale) {
67
- const localeKey = getElementCdsPersistenceName (entity.elements.locale.name/*usually 'LOCALE'*/, naming)
68
- _import.import_settings.include_filter.push ({ [localeKey]: locale })
71
+ const localeKey = getElementCdsPersistenceName(entity.elements.locale.name/*usually 'LOCALE'*/, naming)
72
+ _import.import_settings.include_filter.push({ [localeKey]: locale })
69
73
  }
70
74
  }
71
75
 
72
- tabledata.imports.push (_import)
76
+ tabledata.imports.push(_import)
73
77
  const suffix = '.hdbtabledata'
74
78
  return [
75
79
  tabledata, {
76
80
  name: baseFileName, suffix,
77
81
  file: baseFileName + suffix,
78
- folder: (baseDir||dir), // as metadata, add the dir to which the csvs are relative to
82
+ folder: (baseDir || dir), // as metadata, add the dir to which the csvs are relative to
79
83
  csvFolder: dir
80
84
  }
81
85
  ]
82
86
  }
83
87
 
84
- function _entity4 (name, csn) {
85
- const entity = csn.definitions [name]
88
+ function _entity4(name, csn) {
89
+ const entity = csn.definitions[name]
86
90
  if (!entity) {
87
- if (/(.+)[._]texts_?/.test (name)) { // 'Books_texts', 'Books_texts_de'
88
- const base = csn.definitions [RegExp.$1]
91
+ if (/(.+)[._]texts_?/.test(name)) { // 'Books_texts', 'Books_texts_de'
92
+ const base = csn.definitions[RegExp.$1]
89
93
  if (base && base.elements && base.elements.texts) {
90
- return _entity4 (base.elements.texts.target, csn)
94
+ return _entity4(base.elements.texts.target, csn)
91
95
  }
92
96
  }
93
97
  return
94
98
  }
95
- if (entity['@cds.persistence.skip'] === true) return _warn (`exclude skipped entity '${name}'`)
99
+ if (entity['@cds.persistence.skip'] === true) return logger.warn(`[hdbtabledata] exclude skipped entity '${name}'`)
96
100
  const p = entity.query && entity.query.SELECT || entity.projection
97
101
  if (p) {
98
102
  let from = p.from
99
103
  if (from && from.ref && from.ref.length === 1) {
100
- return _entity4 (from.ref[0], csn)
104
+ return _entity4(from.ref[0], csn)
101
105
  }
102
106
  }
103
107
  return entity
104
108
  }
105
109
 
106
- function _csvDirs (sources) {
107
- sources = Array.from (new Set(sources)) // uniq
110
+ function _csvDirs(sources) {
111
+ sources = Array.from(new Set(sources)) // uniq
108
112
  const folders = []
109
113
  for (let src of sources) {
110
- for (let data of ['/data','/csv']) {
111
- for (let each of [ src+data, src+'/src'+data, src+'/..'+data ]) {
112
- let folder = path.resolve (cds.root,each)
113
- if (isdir (folder)) folders.push (folder)
114
+ for (let data of ['/data', '/csv']) {
115
+ for (let each of [src + data, src + '/src' + data, src + '/..' + data]) {
116
+ let folder = path.resolve(cds.root, each)
117
+ if (isdir(folder)) folders.push(folder)
114
118
  }
115
119
  }
116
120
  }
117
121
  return folders
118
122
  }
119
123
 
120
- function _csvs (filename,_,allFiles) {
121
- if (filename[0] === '-' || !filename.endsWith ('.csv')) return false
124
+ function _csvs(filename, _, allFiles) {
125
+ if (filename[0] === '-' || !filename.endsWith('.csv')) return false
122
126
  // ignores 'Books_texts.csv' if there is any 'Books_texts_LANG.csv'
123
- if (/(.*)[._]texts\.csv$/.test (filename)) {
127
+ if (/(.*)[._]texts\.csv$/.test(filename)) {
124
128
  const basename = RegExp.$1
125
- const monoLangFiles = allFiles.filter (file => new RegExp(basename+'_texts_').test (file))
129
+ const monoLangFiles = allFiles.filter(file => new RegExp(basename + '_texts_').test(file))
126
130
  if (monoLangFiles.length > 0) {
127
- DEBUG && DEBUG (` - ignoring '${filename}' in favor of [${monoLangFiles}]`) // eslint-disable-line
131
+ logger._debug && logger.debug(`[hdbtabledata] ignoring '${filename}' in favor of [${monoLangFiles}]`)
128
132
  return false
129
133
  }
130
134
  }
@@ -132,8 +136,8 @@ function _csvs (filename,_,allFiles) {
132
136
  }
133
137
 
134
138
  // generator function compliant to what `cds.compile.to` backends can return
135
- function* _toOutput (datas) {
139
+ function* _toOutput(datas) {
136
140
  for (let i = 0; i < datas.length; i++) {
137
- if (datas[i]) yield datas[i]
141
+ if (datas[i]) yield datas[i]
138
142
  }
139
143
  }
@@ -1,15 +1,15 @@
1
- const {fs} = require('@sap/cds-foss')
1
+ const { fs } = require('@sap/cds-foss')
2
2
  const path = require('path')
3
3
  const BuildTaskHandlerInternal = require('../buildTaskHandlerInternal')
4
4
  const { getHanaDbModuleDescriptor } = require('../../mtaUtil')
5
- const { OUTPUT_MODE_RESULT_ONLY, BUILD_OPTION_OUTPUT_MODE, FILE_EXT_CDS } = require('../../constants')
5
+ const { OUTPUT_MODE_RESULT_ONLY, BUILD_OPTION_OUTPUT_MODE, SKIP_HDBTABLEDATA_GENERATION, SKIP_PACKAGE_JSON_GENERATION,
6
+ SKIP_MANIFEST_GENERATION, CONTENT_MANIFEST, CONTENT_PACKAGE_JSON, CONTENT_HDBTABLEDATA } = require('../../constants')
6
7
  const { BuildError, setProperty, relativePaths } = require('../../util')
7
8
  const CSV = require('../../csv-reader')
8
9
  const to_hdbmigration = require('./2migration')
9
10
  const to_hdbtabledata = require('./2tabledata')
10
11
  const { ERROR } = require('../../buildTaskHandler')
11
12
 
12
- const DEBUG = process.env.DEBUG
13
13
  const DEFAULT_COMPILE_DEST_FOLDER = path.normalize("src/gen")
14
14
 
15
15
  const FILE_EXT_CSV = ".csv"
@@ -31,6 +31,11 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
31
31
  dest: this.task.dest,
32
32
  hana: []
33
33
  }
34
+ // set unified option values in order to easy access later on
35
+ this.task.options[CONTENT_MANIFEST] = !this.hasBuildOption(CONTENT_MANIFEST, false) && !this.hasBuildOption(SKIP_MANIFEST_GENERATION, true) ? true : false
36
+ this.task.options[CONTENT_PACKAGE_JSON] = !this.hasBuildOption(CONTENT_PACKAGE_JSON, false) && !this.hasBuildOption(SKIP_PACKAGE_JSON_GENERATION, true) ? true : false
37
+ this.task.options[CONTENT_HDBTABLEDATA] = !this.hasBuildOption(CONTENT_HDBTABLEDATA, false) && !this.hasBuildOption(SKIP_HDBTABLEDATA_GENERATION, true) ? true : false
38
+
34
39
  this.task.options.compileDest = path.resolve(this.task.dest, this.task.options.compileDest || DEFAULT_COMPILE_DEST_FOLDER)
35
40
  }
36
41
 
@@ -60,13 +65,13 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
60
65
  await this._writeHdiConfig(plugins)
61
66
  await this._writeHdiNamespace()
62
67
 
63
- if (!this.hasBuildOption("skipHdbtabledataGeneration", true)) {
68
+ if (this.hasBuildOption(CONTENT_HDBTABLEDATA, true)) {
64
69
  await this._compileToHdbtabledata(model, dest)
65
70
  }
66
- if (!this.hasBuildOption("skipPackageJsonGeneration", true)) {
71
+ if (this.hasBuildOption(CONTENT_PACKAGE_JSON, true)) {
67
72
  await this._writePackageJson()
68
73
  }
69
- if (!this.hasBuildOption("skipManifestGeneration", true)) {
74
+ if (this.hasBuildOption(CONTENT_MANIFEST, true)) {
70
75
  await this._writeManifestYml()
71
76
  await this._writeCfIgnore()
72
77
  }
@@ -97,14 +102,15 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
97
102
  const dbCsvDir = path.join(src, "csv")
98
103
  const dbDataDir = path.join(src, "data")
99
104
  const csvDirs = [dbCsvDir, dbDataDir]
105
+ const regex = RegExp('\\.cds$|\\.csv$|\\.hdbtabledata$')
106
+ const regexData = RegExp('\\.csv$|\\.hdbtabledata$')
100
107
 
101
108
  await super.copyNativeContent(src, dest, (entry) => {
102
109
  if (fs.statSync(entry).isDirectory()) {
103
110
  return true // using common filter for folders
104
111
  }
105
- const extName = path.extname(entry)
106
- return (extName !== FILE_EXT_CSV && extName !== FILE_EXT_HDBTABLEDATA && extName !== FILE_EXT_CDS && entry !== this.env.build.outputfile) ||
107
- ((extName === FILE_EXT_CSV || extName === FILE_EXT_HDBTABLEDATA) && !entry.startsWith(dbCsvDir) && !entry.startsWith(dbCsvDir))
112
+ return (!regex.test(entry) && entry !== this.env.build.outputfile) ||
113
+ (regexData.test(entry) && !entry.startsWith(dbCsvDir) && !entry.startsWith(dbCsvDir))
108
114
  }) || []
109
115
 
110
116
  // handle *.csv and *.hdbtabledata located in '<dbSrc>/data' and '<dbSrc>/csv' folder
@@ -113,8 +119,7 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
113
119
  if (fs.statSync(entry).isDirectory()) {
114
120
  return false
115
121
  }
116
- const extName = path.extname(entry)
117
- return (extName === FILE_EXT_CSV || extName === FILE_EXT_HDBTABLEDATA)
122
+ return regexData.test(entry)
118
123
  }))
119
124
  }, [])
120
125
 
@@ -162,8 +167,9 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
162
167
 
163
168
  const promises = []
164
169
  const relDest = path.relative(this.task.dest, this.task.options.compileDest)
170
+ const options = { ...this.options(), dirs: csvDirs, baseDir: this.task.options.compileDest }
165
171
 
166
- const tableDatas = await to_hdbtabledata(model, { dirs: csvDirs, baseDir: this.task.options.compileDest })
172
+ const tableDatas = await to_hdbtabledata(model, options)
167
173
  for (let [tableData, { file, csvFolder }] of tableDatas) {
168
174
  // create .hdbtabledata side-by-side if .csv is contained in 'src/gen/**' subfolder
169
175
  // otherwise create in 'src/gen'
@@ -261,8 +267,8 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
261
267
  if (!this.hasBuildOption(BUILD_OPTION_OUTPUT_MODE, OUTPUT_MODE_RESULT_ONLY)) {
262
268
  promises.push(this.write(content).to(path.join(dbSrcDir, file)))
263
269
  }
264
- } else if (DEBUG) {
265
- this.logger.log(`[cds] - no change, keep existing ${file}`)
270
+ } else {
271
+ this.logger._debug && this.logger.debug(`no change, keep existing ${file}`)
266
272
  }
267
273
  } else {
268
274
  this._result.hana.push(path.join(relDestDir, file))
@@ -294,8 +300,8 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
294
300
  const packageJson = path.join(this.task.src, "package.json")
295
301
  const exists = await fs.pathExists(packageJson)
296
302
 
297
- if (DEBUG && exists) {
298
- this.logger.log(`[cds] - skip create [${relativePaths(this.buildOptions.root, packageJson)}], already existing`)
303
+ if (exists) {
304
+ this.logger._debug && this.logger.debug(`skip create [${relativePaths(this.buildOptions.root, packageJson)}], already existing`)
299
305
  }
300
306
  if (this.isStagingBuild() && !exists) {
301
307
  const content = await this._readTemplateAsJson(FILE_NAME_PACKAGE_JSON)
@@ -354,9 +360,7 @@ class HanaModuleBuilder extends BuildTaskHandlerInternal {
354
360
  return
355
361
  }
356
362
  if (await fs.pathExists(path.join(this.task.src, FILE_NAME_MANIFEST_YML)) || await fs.pathExists(path.join(this.task.src, 'manifest.yml'))) {
357
- if (DEBUG) {
358
- this.logger.log('[cds] - skip cf manifest generation, already existing')
359
- }
363
+ this.logger.debug('skip cf manifest generation, already existing')
360
364
  return
361
365
  }
362
366
  try {
@@ -373,10 +377,8 @@ applications:
373
377
  services:
374
378
  - ${descriptor.hdiServiceName}`
375
379
 
376
- if (DEBUG) {
377
- this.logger.log("[cds] - Cloud Foundry service binding required for HDI container. To create a service use CF command")
378
- this.logger.log(` cf cs hana hdi-shared ${descriptor.hdiServiceName}`)
379
- }
380
+ this.logger.debug("Cloud Foundry service binding required for HDI container. To create a service use CF command")
381
+ this.logger._debug && this.logger.debug(` cf cs hana hdi-shared ${descriptor.hdiServiceName}`)
380
382
 
381
383
  await this.write(MANIFEST_YML_CONTENT).to(path.join(this.task.dest, FILE_NAME_MANIFEST_YML))
382
384
  } catch (e) {
@@ -413,11 +415,11 @@ applications:
413
415
  return fs.statSync(res).isFile() && path.extname(res) === FILE_EXT_HDBMIGRATIONTABLE
414
416
  })
415
417
  if (migrationTableFiles.length > 0) {
416
- const { MigrationTableParser } = require('./migrationtable')
418
+ const parser = require('./migrationtable')
417
419
 
418
420
  await Promise.all(migrationTableFiles.map(async file => {
419
421
  try {
420
- const tableModel = await MigrationTableParser.read(file)
422
+ const tableModel = await parser.read(file)
421
423
  if (tableModel && /^>>>>>/m.test(tableModel.migrations.toString())) {
422
424
  this.pushMessage(`Current model changes require manual resolution. See migration file ${path.relative(this.buildOptions.root, file)} for further details.`, ERROR)
423
425
  }
@@ -1,4 +1,4 @@
1
- const {fs} = require('@sap/cds-foss')
1
+ const { fs } = require('@sap/cds-foss')
2
2
 
3
3
  class MigrationTableParser {
4
4
  constructor() {
@@ -27,15 +27,15 @@ class MigrationTableParser {
27
27
 
28
28
  _validate(lines) {
29
29
  let isTableBegin = false, isTableEnd = false, isMigration = false
30
- let version = -1
31
- lines.forEach(line => {
32
- if (/^\s*== version=\d+\s*$/.test(line)) {
33
- version = MigrationTableParser._parseVersionNumber(line)
30
+ let tVersion, mVersion = -1
31
+ for (let idx = 0; idx < lines.length; idx++) {
32
+ if (MigrationTableParser._isVersionMarker(lines[idx])) {
33
+ tVersion = MigrationTableParser._parseVersionNumber(lines[idx])
34
34
  if (isTableBegin || isTableEnd || isMigration) {
35
35
  throw new Error(`Invalid format, version defintion must be very first statement`)
36
36
  }
37
- } else if (/^\s*COLUMN TABLE\s/.test(line)) {
38
- if (version === -1) {
37
+ } else if (/^\s*COLUMN TABLE\s/.test(lines[idx])) {
38
+ if (tVersion === -1) {
39
39
  throw new Error(`Invalid format, version entry not complying to format '^== version=d+'`)
40
40
  }
41
41
  if (isTableBegin) {
@@ -45,55 +45,66 @@ class MigrationTableParser {
45
45
  throw new Error(`Invalid format, migrations must not be mixed with COLUMN TABLE definitions`)
46
46
  }
47
47
  isTableBegin = true
48
- } else if (isTableBegin && /^\s*\)\s*$/.test(line)) {
49
- if (isTableEnd) {
50
- throw new Error(`Invalid format, multiple COLUMN TABLE matching closing brackets found`)
51
- }
52
- if (isMigration) {
53
- throw new Error(`Invalid format, migrations must not be mixed with COLUMN TABLE definitions`)
54
- }
55
- isTableEnd = true
56
- } else if (!isMigration && /^\s*== migration=\d+\s*$/.test(line)) {
57
- if (!new RegExp(`^\\s*== migration=${version}\\s*$`).test(line)) {
58
- throw new Error(`Invalid format, migration and table version number do not match'`)
59
- }
48
+ } else if (MigrationTableParser._isMigrationMarker(lines[idx])) {
49
+ const version = MigrationTableParser._parseVersionNumber(lines[idx])
60
50
  if (version === -1) {
61
51
  throw new Error(`Invalid format, migration entry not complying to format '^== version=d+'`)
62
52
  }
63
- if (!isTableBegin) {
64
- throw new Error(`Invalid format, COLUMN TABLE missing`)
53
+ if (version > mVersion) {
54
+ mVersion = version
65
55
  }
66
- if (!isTableEnd) {
67
- throw new Error(`Invalid format, COLUMN TABLE matching closing bracket missing`)
56
+ if (!isMigration) {
57
+ if (!isTableBegin) {
58
+ throw new Error(`Invalid format, COLUMN TABLE statement missing`)
59
+ }
60
+ // back search for end table
61
+ for (let tIdx = idx - 1; tIdx > 0; tIdx--) {
62
+ if (MigrationTableParser._isDDL(lines[tIdx])
63
+ || MigrationTableParser._isComment(lines[tIdx])) {
64
+ isTableEnd = true
65
+ break
66
+ }
67
+ }
68
+ isMigration = true
68
69
  }
69
- isMigration = true
70
+ } else if (isTableBegin && !isMigration && idx + 1 === lines.length) {
71
+ isTableEnd = true
70
72
  }
71
- })
73
+ }
72
74
  if (!isTableBegin) {
73
75
  throw new Error(`Invalid format, COLUMN TABLE statement missing`)
74
76
  }
75
77
  if (!isTableEnd) {
76
- throw new Error(`Invalid format, COLUMN TABLE closing bracket missing`)
78
+ throw new Error(`Invalid format, COLUMN TABLE statement not correctly terminated`)
77
79
  }
78
- if (!isMigration && version > 1) {
79
- throw new Error(`Invalid format, '== migration=${version}' entry missing`)
80
+ if (!isMigration && tVersion > 1) {
81
+ throw new Error(`Invalid format, '== migration=${tVersion}' entry missing`)
82
+ }
83
+ if (mVersion !== -1 && mVersion !== tVersion) {
84
+ throw new Error(`Invalid format, migration version ${mVersion} does not match table version ${tVersion}`)
80
85
  }
81
86
  }
82
87
 
83
88
  _parseTable(lines) {
84
89
  const format = { startLine: -1, endLine: -1 }
85
90
  for (let idx = 0; idx < lines.length; idx++) {
86
- let line = lines[idx]
87
- if (format.startLine === -1 && /^\s*== version=\d+\s*$/.test(line)) {
88
- format.startLine = idx
91
+ if (format.startLine === -1) {
92
+ if (MigrationTableParser._isVersionMarker(lines[idx])) {
93
+ format.startLine = idx
94
+ }
89
95
  } else if (format.endLine === -1) {
90
- if (idx + 1 === lines.length || /^\s*== migration=\s*$/.test(line)) {
91
- // back search for end table
92
- for (let tableIdx = idx; tableIdx > format.startLine; tableIdx--) {
93
- if (/^\s*\)\s*$/.test(lines[tableIdx])) {
94
- format.endLine = tableIdx
95
- break
96
- }
96
+ let tIdx = -1
97
+ if (MigrationTableParser._isMigrationMarker(lines[idx])) {
98
+ tIdx = idx - 1
99
+ } else if (idx + 1 === lines.length) {
100
+ tIdx = idx
101
+ }
102
+ // back search for end of table, comments belong to table
103
+ for (; tIdx > format.startLine; tIdx--) {
104
+ if (MigrationTableParser._isDDL(lines[tIdx])
105
+ || MigrationTableParser._isComment(lines[tIdx])) {
106
+ format.endLine = tIdx
107
+ break
97
108
  }
98
109
  }
99
110
  } else {
@@ -101,7 +112,7 @@ class MigrationTableParser {
101
112
  }
102
113
  }
103
114
  if (format.startLine === -1) {
104
- throw new Error(`Invalid format: .hdbmigrationtable content version not found`)
115
+ throw new Error(`Invalid format, '== version=' entry missing`)
105
116
  }
106
117
  return new MigrationTable(lines, format)
107
118
  }
@@ -109,10 +120,9 @@ class MigrationTableParser {
109
120
  _parseMigrations(lines, table) {
110
121
  const migrations = []
111
122
  let format = { startLine: -1, endLine: -1 }
112
- for (let idx = table.lines.length; idx < lines.length; idx++) { //next line of table statement
113
- let line = lines[idx]
123
+ for (let idx = table.lines.length; idx < lines.length; idx++) {
114
124
  let nextMigration = false
115
- if (/^\s*== migration=\d+\s*$/.test(line)) {
125
+ if (MigrationTableParser._isMigrationMarker(lines[idx])) {
116
126
  if (format.startLine === -1) {
117
127
  format.startLine = idx
118
128
  } else {
@@ -120,25 +130,42 @@ class MigrationTableParser {
120
130
  }
121
131
  }
122
132
  if (format.startLine !== -1 && (nextMigration || (idx + 1) === lines.length)) {
123
- // back search for end migration
124
- for (let migrationIdx = (idx + 1) < lines.length ? idx - 1 : idx; migrationIdx > format.startLine; migrationIdx--) {
125
- if (!/^\s*$|^\s*\)\s*$/.test(lines[migrationIdx])) {
126
- format.endLine = migrationIdx
127
- migrations.push(new Migration(lines, format))
128
- if (nextMigration !== -1) {
129
- format = { startLine: idx, endLine: -1 }
130
- }
133
+ // back search for end of migration, comments belong to former migration
134
+ for (let mIdx = nextMigration ? idx - 1 : idx; mIdx > format.startLine; mIdx--) {
135
+ if (MigrationTableParser._isDDL(lines[mIdx])
136
+ || MigrationTableParser._isComment(lines[mIdx])) {
137
+ format.endLine = mIdx
131
138
  break
132
139
  }
133
140
  }
141
+ migrations.push(new Migration(lines, format))
142
+ if (nextMigration) {
143
+ format = { startLine: idx, endLine: -1 }
144
+ }
134
145
  }
135
146
  }
136
147
  return new Migrations(migrations)
137
148
  }
138
149
 
139
- static _isSqlStatement(line) {
140
- // lines that do not represent comments are treated as valid DDL statements
141
- return !/^\s*--|^\s*==/.test(line)
150
+ // any lines that do not start with a comment or conflict marker and do not represent version tags
151
+ static _isDDL(line) {
152
+ return !/^\s*--|^\s*==|^\s*$|^\s*>>>>>/.test(line)
153
+ }
154
+
155
+ static _isComment(line) {
156
+ return /^\s*--/.test(line)
157
+ }
158
+
159
+ static _isConflictMarker(line) {
160
+ return /^\s*>>>>>/.test(line)
161
+ }
162
+
163
+ static _isVersionMarker(line) {
164
+ return /^\s*== version=\d+\s*$/.test(line)
165
+ }
166
+
167
+ static _isMigrationMarker(line) {
168
+ return /^\s*== migration=\d+\s*$/.test(line)
142
169
  }
143
170
 
144
171
  static _parseVersionNumber(line) {
@@ -154,7 +181,6 @@ class MigrationTableParser {
154
181
  }
155
182
  }
156
183
 
157
-
158
184
  /**
159
185
  * Model representation of an entire .hdbmigrationtable file.
160
186
  * <p>
@@ -269,10 +295,6 @@ class MigrationTable {
269
295
  this._versionNumber = newVersion
270
296
  }
271
297
 
272
- get sql() {
273
- return this._lines.filter(line => MigrationTableParser._isSqlStatement(line)).join('\n')
274
- }
275
-
276
298
  get lines() {
277
299
  return this._lines
278
300
  }
@@ -288,7 +310,7 @@ class MigrationTable {
288
310
 
289
311
  class Migrations {
290
312
  constructor(migrations = []) {
291
- this._migrations = migrations
313
+ this._migrations = migrations.sort((a, b) => b.versionNumber - a.versionNumber)
292
314
  }
293
315
  get versionNumber() {
294
316
  return this._migrations.length > 0 ? this._migrations[0].versionNumber : 1
@@ -327,7 +349,7 @@ class Migration {
327
349
  this._lines = lines
328
350
  }
329
351
  this._versionNumber = MigrationTableParser._parseVersionNumber(this.lines[0])
330
- this._changeset = this._lines.filter(line => MigrationTableParser._isSqlStatement(line))
352
+ this._changeset = this._lines.filter(line => !MigrationTableParser._isMigrationMarker(line))
331
353
  }
332
354
 
333
355
  /**
@@ -359,6 +381,14 @@ class Migration {
359
381
  return this._changeset
360
382
  }
361
383
 
384
+ /**
385
+ * Returns the DDL statements of this changeset. Any lines that do not start with a comment or conflict marker
386
+ * and do not represent version tags are treated as valid DDL statements.
387
+ */
388
+ get ddl() {
389
+ return this.changeset.filter(line => MigrationTableParser._isDDL(line))
390
+ }
391
+
362
392
  /**
363
393
  * Returns the string representation of this migration.
364
394
  */
@@ -371,10 +401,4 @@ class Migration {
371
401
  }
372
402
  }
373
403
 
374
- module.exports = {
375
- MigrationTableParser: new MigrationTableParser(),
376
- MigrationTableModel,
377
- MigrationTable,
378
- Migrations,
379
- Migration
380
- }
404
+ module.exports = new MigrationTableParser()