@sap/cds-dk 6.1.4 → 6.2.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/CHANGELOG.md +35 -1
- package/bin/build/all.js +5 -5
- package/bin/cds.js +7 -5
- package/bin/deploy/index.js +3 -2
- package/bin/deploy/to-sqlite.js +4 -3
- package/bin/help.js +4 -0
- package/bin/init.js +2 -1
- package/bin/login.js +23 -9
- package/bin/mock.js +1 -0
- package/bin/pull.js +24 -19
- package/bin/push.js +20 -9
- package/bin/subscribe.js +40 -10
- package/bin/watch.js +15 -10
- package/lib/bind/kubernetesCredentialsProvider.js +1 -0
- package/lib/client/activate.js +42 -51
- package/lib/client/auth_manager.js +7 -8
- package/lib/client/base_command.js +5 -2
- package/lib/client/extend.js +33 -38
- package/lib/client/helper/cf_helper.js +11 -11
- package/lib/client/helper/dependency_helper.js +2 -2
- package/lib/client/helper/errors.js +4 -3
- package/lib/client/helper/logging.js +17 -23
- package/lib/client/params.js +5 -5
- package/lib/client/pull.js +19 -21
- package/lib/client/push.js +16 -15
- package/lib/client/settings_manager.js +54 -55
- package/lib/compile/openapi/csdl2openapi.js +18 -12
- package/lib/import/message.js +1 -1
- package/lib/import/odata/metadataconverter/MetadataConverter.js +23 -17
- package/lib/import/odata/v2parser.js +43 -17
- package/lib/import/odata/v4parser.js +77 -22
- package/lib/init/constants.js +5 -2
- package/lib/init/index.js +16 -4
- package/lib/init/template/_merging/registry-mta.js +13 -0
- package/lib/init/template/approuter/index.js +9 -0
- package/lib/{k8s/connectivity/templates/connectivity.yaml → init/template/auditlog/files/auditlog.yaml} +0 -0
- package/lib/init/template/auditlog/index.js +46 -27
- package/lib/{k8s/connectivity/templates → init/template/connectivity/files}/connectivity-binding.yaml +0 -0
- package/lib/{k8s/connectivity/templates → init/template/connectivity/files}/connectivity-proxy-info.yaml +1 -1
- package/lib/init/template/connectivity/files/connectivity.yaml +1 -0
- package/lib/init/template/connectivity/files/values.yaml.hbs +26 -0
- package/lib/init/template/connectivity/index.js +41 -0
- package/lib/init/template/data/index.js +8 -8
- package/lib/init/template/destinations/files/destination.yaml +1 -0
- package/lib/init/template/destinations/index.js +44 -0
- package/lib/init/template/enterprise-messaging/files/cds.package.json +10 -0
- package/lib/init/template/enterprise-messaging/files/cds.profile.package.json.hbs +12 -0
- package/lib/init/template/enterprise-messaging/files/cdsrc.json +8 -0
- package/lib/init/template/enterprise-messaging/files/cdsrc.json.hbs +10 -0
- package/lib/init/template/enterprise-messaging/files/event-mesh.yaml +1 -0
- package/lib/init/template/enterprise-messaging/files/mta.yaml.hbs +13 -0
- package/lib/init/template/enterprise-messaging/files/values.yaml +8 -0
- package/lib/init/template/enterprise-messaging/index.js +56 -0
- package/lib/init/template/extensibility/files/cdsrc.json +5 -0
- package/lib/init/template/extensibility/files/cdsrc.json.hbs +7 -0
- package/lib/init/template/extensibility/index.js +18 -13
- package/lib/{k8s/hana-deployer/templates → init/template/hana/files}/hana-deployer-job.yaml +0 -0
- package/lib/init/template/hana/files/java/{cdsrc.json.hbs → cds.json.hbs} +0 -0
- package/lib/init/template/hana/files/java/{profile.cdsrc.json.hbs → cds.profile.json.hbs} +0 -0
- package/lib/init/template/hana/files/java/cds.shortcut.json.hbs +5 -0
- package/lib/init/template/hana/files/java/cds.shortcut.profile.json.hbs +8 -0
- package/lib/init/template/hana/files/java/db/package.json +2 -1
- package/lib/init/template/hana/files/mta.yaml.hbs +6 -0
- package/lib/init/template/hana/files/nodejs/{cds.package.json.hbs → cds.json.hbs} +0 -0
- package/lib/init/template/hana/files/nodejs/{cds.profile.package.json.hbs → cds.profile.json.hbs} +0 -0
- package/lib/init/template/hana/files/nodejs/cds.shortcut.json.hbs +7 -0
- package/lib/init/template/hana/files/nodejs/cds.shortcut.profile.json.hbs +10 -0
- package/lib/init/template/hana/files/values.yaml.hbs +19 -0
- package/lib/init/template/hana/index.js +29 -29
- package/lib/{k8s → init/template/helm}/Model.d.ts +0 -0
- package/lib/{k8s/resources → init/template}/helm/chart/templates/NOTES.txt +0 -0
- package/lib/{k8s/resources → init/template}/helm/chart/templates/_deployment_helpers.tpl +0 -0
- package/lib/{k8s/resources → init/template}/helm/chart/templates/_helpers.tpl +4 -5
- package/lib/{k8s/resources → init/template}/helm/chart/templates/_sapcp_helpers.tpl +0 -0
- package/lib/{k8s/resources/helm/chart/schemas → init/template/helm/chart}/values.root.schema.json +0 -0
- package/lib/{k8s/resources/helm/chart/schemas → init/template/helm/chart}/values.schema.json +0 -0
- package/lib/init/template/helm/files/Chart.yaml.hbs +13 -0
- package/lib/init/template/helm/files/values.yaml.hbs +26 -0
- package/lib/init/template/helm/index.js +74 -14
- package/lib/{k8s/deployment → init/template/helm}/subchart/Chart.yaml +0 -0
- package/lib/{k8s/deployment → init/template/helm}/subchart/templates/_helpers.tpl +0 -0
- package/lib/{k8s/deployment → init/template/helm}/subchart/templates/api-rule.yaml +0 -0
- package/lib/{k8s/deployment → init/template/helm}/subchart/templates/deployment.yaml +0 -0
- package/lib/{k8s/deployment → init/template/helm}/subchart/templates/network-policy.yaml +0 -0
- package/lib/{k8s/deployment → init/template/helm}/subchart/templates/pod-disruption-budget.yaml +0 -0
- package/lib/{k8s/deployment → init/template/helm}/subchart/templates/secret.yaml +0 -0
- package/lib/{k8s/deployment → init/template/helm}/subchart/templates/service-binding.yaml +0 -0
- package/lib/{k8s/deployment → init/template/helm}/subchart/templates/service.yaml +0 -0
- package/lib/{k8s/deployment → init/template/helm}/subchart/values-lint.yaml +0 -0
- package/lib/{k8s/deployment → init/template/helm}/subchart/values.md +0 -0
- package/lib/{k8s/deployment → init/template/helm}/subchart/values.schema.json +0 -0
- package/lib/{k8s/deployment → init/template/helm}/subchart/values.yaml +0 -0
- package/lib/{k8s/html5-apps-deployer/templates → init/template/html5-repo/files}/_html5_deployment_helpers.tpl +0 -0
- package/lib/{k8s/html5-apps-deployer/templates → init/template/html5-repo/files}/html5-apps-deployer-job.yaml +1 -1
- package/lib/{k8s/html5-apps-repo-host/templates → init/template/html5-repo/files}/html5-apps-repo-host.yaml +0 -0
- package/lib/init/template/html5-repo/index.js +59 -0
- package/lib/init/template/kibana-logging/index.js +8 -0
- package/lib/init/template/lint/index.js +1 -4
- package/lib/init/template/mta/files/mta.yaml.hbs +3 -2
- package/lib/init/template/mta/index.js +6 -1
- package/lib/init/template/multitenancy/files/java/.cdsrc.json +3 -0
- package/lib/init/template/multitenancy/files/java/package.json +2 -6
- package/lib/{k8s/multitenancy/templates → init/template/multitenancy/files}/saas-registry.yaml +3 -3
- package/lib/init/template/multitenancy/files/xsuaa.yaml +1 -0
- package/lib/init/template/multitenancy/index.js +53 -26
- package/lib/init/template/nodejs/files/.gitignore.hbs +1 -0
- package/lib/init/template/templateBase.js +1 -1
- package/lib/init/template/xsuaa/files/values.yaml.hbs +10 -0
- package/lib/init/template/xsuaa/files/xsuaa.yaml +1 -0
- package/lib/init/template/xsuaa/index.js +34 -22
- package/lib/init/util/fsUtil.js +11 -5
- package/lib/init/util/projectReader.js +24 -32
- package/lib/init/util/templateUtil.js +17 -5
- package/lib/lint/Linter.js +9 -17
- package/lib/lint/checks.js +0 -1
- package/lib/lint/generators.js +2 -192
- package/lib/lint/io.js +1 -3
- package/lib/watch/node-watch.js +2 -2
- package/npm-shrinkwrap.json +1412 -791
- package/package.json +5 -4
- package/lib/init/template/helm/update/index.js +0 -28
- package/lib/k8s/ChartAdder.js +0 -75
- package/lib/k8s/ChartBuilder.js +0 -192
- package/lib/k8s/Context.js +0 -71
- package/lib/k8s/ImageUtil.js +0 -21
- package/lib/k8s/auditlog/index.js +0 -29
- package/lib/k8s/auditlog/templates/auditlog.yaml +0 -1
- package/lib/k8s/buildModel.js +0 -68
- package/lib/k8s/connectivity/index.js +0 -80
- package/lib/k8s/deployment/index.js +0 -79
- package/lib/k8s/deployment/subchart-override/templates/_web_application_helpers.tpl +0 -81
- package/lib/k8s/destinations/index.js +0 -40
- package/lib/k8s/destinations/lib.js +0 -16
- package/lib/k8s/destinations/templates/destinations.yaml +0 -1
- package/lib/k8s/docker/push.js +0 -8
- package/lib/k8s/event-mesh/index.js +0 -53
- package/lib/k8s/event-mesh/lib.js +0 -9
- package/lib/k8s/event-mesh/templates/event-mesh.yaml +0 -1
- package/lib/k8s/hana-deployer/index.js +0 -48
- package/lib/k8s/helm/Release.js +0 -80
- package/lib/k8s/html5-apps-deployer/index.js +0 -53
- package/lib/k8s/html5-apps-repo-host/index.js +0 -26
- package/lib/k8s/html5-apps-repo-host/lib.js +0 -16
- package/lib/k8s/index.js +0 -153
- package/lib/k8s/modules.js +0 -36
- package/lib/k8s/multitenancy/index.js +0 -24
- package/lib/k8s/multitenancy/lib.js +0 -25
- package/lib/k8s/readProject.js +0 -14
- package/lib/k8s/util.js +0 -186
- package/lib/k8s/wrapFeature.js +0 -55
- package/lib/k8s/xsuaa/index.js +0 -64
- package/lib/k8s/xsuaa/lib.js +0 -17
- package/lib/k8s/xsuaa/templates/xsuaa.yaml +0 -1
package/lib/client/push.js
CHANGED
|
@@ -1,34 +1,35 @@
|
|
|
1
|
+
const cds = require ('../cds'), { local } = cds.utils
|
|
1
2
|
const fs = require('fs');
|
|
2
3
|
const { join } = require('path');
|
|
3
4
|
const axios = require('axios');
|
|
4
5
|
|
|
5
6
|
const { login } = require('./auth_manager');
|
|
6
7
|
const BaseCommand = require('./base_command');
|
|
7
|
-
const
|
|
8
|
-
const { CliError } = require('./helper/errors');
|
|
8
|
+
const { CliError } = require('./helper/errors'); // REVISIT: add and use a this.error() method instead
|
|
9
9
|
|
|
10
10
|
module.exports = class Push extends BaseCommand {
|
|
11
11
|
|
|
12
12
|
static async run(paramValues) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
13
|
+
const params = await login(paramValues);
|
|
14
|
+
const projectFolder = params.get('projectFolder');
|
|
15
|
+
const tar = join(projectFolder, 'gen', 'extension.tgz')
|
|
16
|
+
const url = params.get('appUrl')
|
|
17
|
+
const subdomain = params.get('subdomain') // REVISIT: Why are subdomains not transparently encoded in URLs?
|
|
18
|
+
const target = { url }; if (subdomain) target.subdomain = subdomain
|
|
19
|
+
const source = { src: local(tar) }
|
|
20
|
+
console.log(`\nPushing extension from`, source, 'to', target);
|
|
21
|
+
|
|
22
|
+
const tgz = fs.readFileSync(tar, { encoding: 'base64' });
|
|
23
|
+
const extensionName = this.getExtensionName(projectFolder);
|
|
24
|
+
await this.pushTgz(params, tgz, extensionName);
|
|
25
|
+
console.log ('Activation succeeded.\n')
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
static getExtensionName(projectFolder) {
|
|
28
29
|
try {
|
|
29
30
|
return require(join(projectFolder, 'package.json')).name;
|
|
30
31
|
} catch (error) {
|
|
31
|
-
throw new CliError(`Extension project at ${projectFolder} is missing package.json
|
|
32
|
+
throw new CliError(`Extension project at ${projectFolder} is missing package.json file`, error);
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
|
|
@@ -1,36 +1,37 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const os = require('os');
|
|
3
3
|
const path = require('path');
|
|
4
|
+
const axios = require('axios');
|
|
4
5
|
|
|
5
6
|
const requireGlobal = require('./helper/dependency_helper');
|
|
6
7
|
const keytar = requireGlobal('keytar');
|
|
7
8
|
|
|
9
|
+
const log = require('./helper/logging');
|
|
8
10
|
const { getAppFromSuggestions, getAppName, getSubdomain } = require('./helper/cf_helper');
|
|
9
11
|
const isBas = require('./helper/env_helper');
|
|
10
|
-
const Logger = require('./helper/logging');
|
|
11
12
|
const Question = require('./helper/question');
|
|
12
13
|
const { CliError } = require('./helper/errors');
|
|
13
14
|
const { httpSchema, httpsSchema, extractSubdomain, schemaRegex, localhostRegex } = require('./helper/url_helper');
|
|
14
|
-
const { ParamCollection, Persistence } = require('./params');
|
|
15
15
|
const { capitalize } = require('./helper/string_helper');
|
|
16
|
-
|
|
16
|
+
|
|
17
|
+
const { ParamCollection, Persistence } = require('./params');
|
|
17
18
|
|
|
18
19
|
const CONFIG_SUBDIRS = {
|
|
19
20
|
linux: '.config',
|
|
20
21
|
darwin: path.join('Library', 'Preferences'),
|
|
21
22
|
win32: 'AppData'
|
|
22
|
-
}
|
|
23
|
+
};
|
|
23
24
|
|
|
24
25
|
const TOKEN_STORAGE_DESC = {
|
|
25
26
|
plain: 'plain-text storage',
|
|
26
27
|
keyring: 'keyring'
|
|
27
|
-
}
|
|
28
|
+
};
|
|
28
29
|
|
|
29
30
|
const KEYRING_DESC = {
|
|
30
31
|
linux: 'libsecret',
|
|
31
32
|
darwin: 'Keychain',
|
|
32
33
|
win32: 'Credential Vault'
|
|
33
|
-
}
|
|
34
|
+
};
|
|
34
35
|
|
|
35
36
|
const MTX_FULLY_QUALIFIED = 'com.sap.cds.mtx';
|
|
36
37
|
const OAUTH_PATH_OLD_MTX = '/mtx/v1/oauth/token';
|
|
@@ -84,9 +85,9 @@ async function getAppUrlAndSubdomainFromSuggestions(params) {
|
|
|
84
85
|
async function getSubdomainFromCfAndNotify(appName) {
|
|
85
86
|
const subdomain = await getSubdomain(appName);
|
|
86
87
|
if (subdomain) {
|
|
87
|
-
|
|
88
|
+
console.log('Subdomain determined from CF app environment:', subdomain);
|
|
88
89
|
} else {
|
|
89
|
-
|
|
90
|
+
console.warn('Failed to determine subdomain from CF app environment');
|
|
90
91
|
}
|
|
91
92
|
return subdomain;
|
|
92
93
|
}
|
|
@@ -113,12 +114,11 @@ async function getTokenBaseUrl(params, renewUrl) {
|
|
|
113
114
|
await axios.head(getBaseUrl(path).toString());
|
|
114
115
|
existingPath = path;
|
|
115
116
|
break;
|
|
116
|
-
} catch (
|
|
117
|
-
if (
|
|
117
|
+
} catch (error) {
|
|
118
|
+
if (error.response?.status === 404) {
|
|
118
119
|
continue;
|
|
119
|
-
} else {
|
|
120
|
-
throw new CliError(`Failed with root cause ${e.message}`);
|
|
121
120
|
}
|
|
121
|
+
throw error;
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
existingPath = existingPath ?? OAUTH_PATH_OLD_MTX;
|
|
@@ -129,16 +129,15 @@ function getKeyringAccountName(params) {
|
|
|
129
129
|
return `${params.get('appUrl')}|${params.get('subdomain')}`;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
|
|
133
132
|
function logConfigPaths() {
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
log.debug(`Settings are stored in ${CONFIG.paths.settings}`);
|
|
134
|
+
log.debug(`Authentication data is stored in ${CONFIG.paths.auth} (${TOKEN_STORAGE_DESC.plain}) or ${CONFIG.keyringDesignation} (${TOKEN_STORAGE_DESC.keyring})`);
|
|
136
135
|
}
|
|
137
136
|
|
|
138
137
|
function notifyLoggedIn(params) {
|
|
139
138
|
return params.get('renewLogin')
|
|
140
|
-
?
|
|
141
|
-
:
|
|
139
|
+
? console.log.bind(console)
|
|
140
|
+
: log.debug;
|
|
142
141
|
}
|
|
143
142
|
|
|
144
143
|
function emptyData(all) {
|
|
@@ -157,14 +156,14 @@ class SettingsManager {
|
|
|
157
156
|
|
|
158
157
|
// Call with 'projectFolder' param as a resolved path.
|
|
159
158
|
static async saveSettings(params) {
|
|
160
|
-
|
|
159
|
+
log.debug(`Saving settings for project ${params.get('projectFolder')}`);
|
|
161
160
|
|
|
162
161
|
const paramsMap = params.toValueMap(Persistence.setting);
|
|
163
162
|
if (localhostRegex.test(params.get('appUrl'))) {
|
|
164
|
-
|
|
163
|
+
console.log(`Saving username${(params.get('isEmptyPassword') ? ' and empty-password hint' : '')} with project settings.`);
|
|
165
164
|
} else {
|
|
166
165
|
// Save only against localhost for security reasons.
|
|
167
|
-
|
|
166
|
+
log.debug('Not saving username because app is not recognized to run on localhost.');
|
|
168
167
|
delete paramsMap.username;
|
|
169
168
|
delete paramsMap.isEmptyPassword;
|
|
170
169
|
}
|
|
@@ -185,7 +184,7 @@ class SettingsManager {
|
|
|
185
184
|
} else if (params.get('tokenStorage') === 'keyring') {
|
|
186
185
|
await this._saveAuthToKeyring(params, params.toValueMap(Persistence.auth));
|
|
187
186
|
} else {
|
|
188
|
-
|
|
187
|
+
console.log('Note: the authentication token is not saved by default. To save the token for later commands, please run `cds login`.');
|
|
189
188
|
}
|
|
190
189
|
}
|
|
191
190
|
|
|
@@ -194,7 +193,7 @@ class SettingsManager {
|
|
|
194
193
|
params.set('projectFolder', projectFolder);
|
|
195
194
|
|
|
196
195
|
const loadedSettings = await this._loadFromFile(projectFolder);
|
|
197
|
-
|
|
196
|
+
log.debug(`Loaded project settings for ${params.get('projectFolder')}: ${loadedSettings.format()}`);
|
|
198
197
|
const loadedAppUrl = loadedSettings.get('appUrl');
|
|
199
198
|
|
|
200
199
|
params.mergeLower(loadedSettings);
|
|
@@ -202,7 +201,7 @@ class SettingsManager {
|
|
|
202
201
|
await this.updateUrls(params, logout, loadedAppUrl);
|
|
203
202
|
await this.checkKeytar(params, logout);
|
|
204
203
|
await this.addAuth(params, logout);
|
|
205
|
-
|
|
204
|
+
log.debug(`Effective project settings: ${params.format()}`);
|
|
206
205
|
}
|
|
207
206
|
|
|
208
207
|
static async updateUrls(params, logout, loadedAppUrl) {
|
|
@@ -215,15 +214,15 @@ class SettingsManager {
|
|
|
215
214
|
} else {
|
|
216
215
|
params.set('appUrl', httpsSchema + appUrl);
|
|
217
216
|
}
|
|
218
|
-
|
|
217
|
+
log.debug(`Added schema to app URL: ${params.get('appUrl')}`);
|
|
219
218
|
}
|
|
220
219
|
}
|
|
221
220
|
if (!logout) {
|
|
222
221
|
if (!params.has('appUrl')) {
|
|
223
|
-
|
|
222
|
+
log.debug('App URL not given');
|
|
224
223
|
await getAppUrlAndSubdomainFromSuggestions(params);
|
|
225
224
|
} else if (!params.has('subdomain') && !localhostRegex.test(params.get('appUrl'))) {
|
|
226
|
-
|
|
225
|
+
log.debug('Subdomain not given');
|
|
227
226
|
const appName = await getAppName(params.get('appUrl'));
|
|
228
227
|
if (appName) {
|
|
229
228
|
params.set('subdomain', await getSubdomainFromCfAndNotify(appName));
|
|
@@ -233,13 +232,13 @@ class SettingsManager {
|
|
|
233
232
|
if (!params.has('appUrl')) {
|
|
234
233
|
throw new CliError('App URL not given. Please specify it or log in to Cloud Foundry and repeat this command.');
|
|
235
234
|
}
|
|
236
|
-
if (!params.has('subdomain')) {
|
|
235
|
+
if (!params.has('subdomain') && !params.has('username')) {
|
|
237
236
|
const subdomain = extractSubdomain(params.get('appUrl'));
|
|
238
237
|
if (!subdomain) {
|
|
239
238
|
throw new CliError('Subdomain not given');
|
|
240
239
|
}
|
|
241
240
|
params.set('subdomain', subdomain);
|
|
242
|
-
|
|
241
|
+
console.log('Subdomain determined from app URL:', subdomain);
|
|
243
242
|
}
|
|
244
243
|
}
|
|
245
244
|
async function renewTokenUrl() {
|
|
@@ -259,7 +258,7 @@ class SettingsManager {
|
|
|
259
258
|
static async checkKeytar(params, logout) {
|
|
260
259
|
if (isBas()) {
|
|
261
260
|
if (params.get('tokenStorage') === 'keyring') {
|
|
262
|
-
|
|
261
|
+
console.log('NOTE:', capitalize(TOKEN_STORAGE_DESC.keyring), 'not supported on SAP Business Application Studio. Switching to', TOKEN_STORAGE_DESC.plain, '.');
|
|
263
262
|
params.set('tokenStorage', 'plain');
|
|
264
263
|
}
|
|
265
264
|
return;
|
|
@@ -268,11 +267,11 @@ class SettingsManager {
|
|
|
268
267
|
return;
|
|
269
268
|
}
|
|
270
269
|
if (params.get('tokenStorage') === 'keyring') {
|
|
271
|
-
throw new CliError(
|
|
272
|
-
`switch to
|
|
270
|
+
throw new CliError(capitalize(TOKEN_STORAGE_DESC.keyring) + ' requested but keytar not installed. ' +
|
|
271
|
+
'Run `npm install -g keytar` or switch to ' + TOKEN_STORAGE_DESC.plain + ' by adding `--plain` (discouraged).');
|
|
273
272
|
}
|
|
274
273
|
if (params.get('renewLogin') || logout) {
|
|
275
|
-
|
|
274
|
+
console.log('Disabling', TOKEN_STORAGE_DESC.keyring, 'functionality: `keytar` module not found. Run `npm install -g keytar` to install it.');
|
|
276
275
|
}
|
|
277
276
|
}
|
|
278
277
|
|
|
@@ -289,7 +288,7 @@ class SettingsManager {
|
|
|
289
288
|
params.set('clientsecret', await Question.askQuestion('clientsecret: ', undefined, true));
|
|
290
289
|
console.log();
|
|
291
290
|
if (params.get('clientsecret') === '') {
|
|
292
|
-
throw new CliError('
|
|
291
|
+
throw new CliError('Clientsecret cannot be empty');
|
|
293
292
|
}
|
|
294
293
|
}
|
|
295
294
|
async function addPasscode() {
|
|
@@ -309,7 +308,7 @@ class SettingsManager {
|
|
|
309
308
|
if (auth.keyring.has('tokenStorage') && auth.plain.has('tokenStorage')) {
|
|
310
309
|
// Both storage places contain data: retrieve from selected or keyring.
|
|
311
310
|
storage = storage || 'keyring';
|
|
312
|
-
|
|
311
|
+
log.debug('WARNING: authentication data found in both kinds of storage. ' +
|
|
313
312
|
`Using data from ${TOKEN_STORAGE_DESC[storage]}; other storage will be cleared when next saving.`);
|
|
314
313
|
|
|
315
314
|
params.merge(auth[storage]);
|
|
@@ -317,7 +316,7 @@ class SettingsManager {
|
|
|
317
316
|
|
|
318
317
|
} else if (storage && auth[other(storage)].has('tokenStorage')) {
|
|
319
318
|
// Selected storage contains no data, but other one does: retrieve from other storage, but earmark migration to selected one.
|
|
320
|
-
|
|
319
|
+
log.debug(`Using authentication data from ${TOKEN_STORAGE_DESC[other(storage)]}; will be migrated to other storage on save.`);
|
|
321
320
|
|
|
322
321
|
params.merge(auth[other(storage)]);
|
|
323
322
|
params.set('tokenStorage', storage);
|
|
@@ -326,7 +325,7 @@ class SettingsManager {
|
|
|
326
325
|
} else {
|
|
327
326
|
// One storage contains data, and there's no conflict with selection.
|
|
328
327
|
const storage = auth.keyring.get('tokenStorage') || auth.plain.get('tokenStorage');
|
|
329
|
-
|
|
328
|
+
log.debug(`Using authentication data from ${TOKEN_STORAGE_DESC[storage]}.`);
|
|
330
329
|
|
|
331
330
|
params.merge(auth[storage]);
|
|
332
331
|
}
|
|
@@ -359,7 +358,7 @@ class SettingsManager {
|
|
|
359
358
|
|
|
360
359
|
setRelevantAuth(auth);
|
|
361
360
|
|
|
362
|
-
if (!params.has('token') && !params.has('passcode') && !logout) {
|
|
361
|
+
if (!params.has('token') && !params.has('passcode') && !params.has('clientsecret') && !logout) {
|
|
363
362
|
await addPasscode();
|
|
364
363
|
}
|
|
365
364
|
}
|
|
@@ -372,11 +371,11 @@ class SettingsManager {
|
|
|
372
371
|
let fromStorage = fromOtherStorage && other(params.get('tokenStorage'))
|
|
373
372
|
const deleteBoth = !fromStorage && allParams.get('tokenStorage') && allParams.get('clearOtherTokenStorage');
|
|
374
373
|
|
|
375
|
-
|
|
374
|
+
log.debug(`Deleting authentication data${fromOtherStorage ? ' from other storage' : ''} for ${target}`);
|
|
376
375
|
|
|
377
376
|
fromStorage = fromStorage || allParams.get('tokenStorage');
|
|
378
377
|
if (!fromStorage) {
|
|
379
|
-
|
|
378
|
+
console.log('Failed to delete authentication data: none found for', target);
|
|
380
379
|
return;
|
|
381
380
|
}
|
|
382
381
|
|
|
@@ -417,9 +416,9 @@ class SettingsManager {
|
|
|
417
416
|
await this.deleteToken(new ParamCollection({ appUrl, subdomain }));
|
|
418
417
|
}
|
|
419
418
|
}
|
|
420
|
-
|
|
419
|
+
console.log('Deleted settings for nonexistent project folders:', deletionFolders.map(folder => ' ' + folder));
|
|
421
420
|
} else {
|
|
422
|
-
|
|
421
|
+
console.log('All settings seem valid');
|
|
423
422
|
}
|
|
424
423
|
}
|
|
425
424
|
|
|
@@ -428,19 +427,19 @@ class SettingsManager {
|
|
|
428
427
|
if (paramValues !== null) {
|
|
429
428
|
paramValuesByFolder[projectFolder] = paramValues;
|
|
430
429
|
await this._saveAllSettingsToFile(paramValuesByFolder);
|
|
431
|
-
|
|
430
|
+
log.debug(`Saved project settings: ${JSON.stringify(paramValues)}`);
|
|
432
431
|
} else {
|
|
433
432
|
delete paramValuesByFolder[projectFolder];
|
|
434
433
|
await this._saveAllSettingsToFile(paramValuesByFolder);
|
|
435
|
-
|
|
434
|
+
console.log('Deleted project settings');
|
|
436
435
|
}
|
|
437
436
|
}
|
|
438
437
|
|
|
439
438
|
static async _saveAllSettingsToFile(paramValuesByFolder) {
|
|
440
|
-
|
|
439
|
+
log.debug(`Saving all settings to ${CONFIG.paths.settings}`);
|
|
441
440
|
await fs.promises.mkdir(path.dirname(CONFIG.paths.settings), { recursive: true })
|
|
442
441
|
await fs.promises.writeFile(CONFIG.paths.settings, JSON.stringify(paramValuesByFolder, null, 2));
|
|
443
|
-
|
|
442
|
+
log.debug('Saved settings');
|
|
444
443
|
}
|
|
445
444
|
|
|
446
445
|
static async _saveAuthToFile(params, authValues) {
|
|
@@ -460,7 +459,7 @@ class SettingsManager {
|
|
|
460
459
|
}
|
|
461
460
|
await fs.promises.mkdir(path.dirname(CONFIG.paths.auth), { recursive: true })
|
|
462
461
|
await fs.promises.writeFile(CONFIG.paths.auth, JSON.stringify(allAuthValues, null, 2));
|
|
463
|
-
|
|
462
|
+
console.log('Deleted authentication data from', location);
|
|
464
463
|
} else {
|
|
465
464
|
notifyLoggedIn(params)(`No authentication data to delete from ${location}`);
|
|
466
465
|
}
|
|
@@ -478,7 +477,7 @@ class SettingsManager {
|
|
|
478
477
|
notifyLoggedIn(params)(`Saved authentication data to ${location}`);
|
|
479
478
|
} else {
|
|
480
479
|
await keytar.deletePassword(MTX_FULLY_QUALIFIED, getKeyringAccountName(params));
|
|
481
|
-
|
|
480
|
+
console.log('Deleted authentication data from', location);
|
|
482
481
|
}
|
|
483
482
|
}
|
|
484
483
|
|
|
@@ -486,7 +485,7 @@ class SettingsManager {
|
|
|
486
485
|
const all = !projectFolder;
|
|
487
486
|
|
|
488
487
|
if (!fs.existsSync(CONFIG.paths.settings)) {
|
|
489
|
-
|
|
488
|
+
log.debug('Settings file not found');
|
|
490
489
|
return emptyData(all);
|
|
491
490
|
}
|
|
492
491
|
|
|
@@ -494,7 +493,7 @@ class SettingsManager {
|
|
|
494
493
|
try {
|
|
495
494
|
settingsByFolder = JSON.parse((await fs.promises.readFile(CONFIG.paths.settings)).toString());
|
|
496
495
|
} catch (err) {
|
|
497
|
-
|
|
496
|
+
log.debug('Empty settings file');
|
|
498
497
|
return emptyData(all);
|
|
499
498
|
}
|
|
500
499
|
|
|
@@ -503,7 +502,7 @@ class SettingsManager {
|
|
|
503
502
|
}
|
|
504
503
|
|
|
505
504
|
if (!settingsByFolder[projectFolder]) {
|
|
506
|
-
|
|
505
|
+
log.debug(`No settings found for project ${projectFolder}`);
|
|
507
506
|
return new ParamCollection();
|
|
508
507
|
}
|
|
509
508
|
|
|
@@ -514,7 +513,7 @@ class SettingsManager {
|
|
|
514
513
|
const all = !appUrl;
|
|
515
514
|
|
|
516
515
|
if (!fs.existsSync(CONFIG.paths.auth)) {
|
|
517
|
-
|
|
516
|
+
log.debug('Authentication-data file not found');
|
|
518
517
|
return emptyData(all);
|
|
519
518
|
}
|
|
520
519
|
|
|
@@ -522,7 +521,7 @@ class SettingsManager {
|
|
|
522
521
|
try {
|
|
523
522
|
allAuth = JSON.parse((await fs.promises.readFile(CONFIG.paths.auth)).toString());
|
|
524
523
|
} catch (err) {
|
|
525
|
-
|
|
524
|
+
log.debug('Empty authentication-data file');
|
|
526
525
|
return emptyData(all);
|
|
527
526
|
}
|
|
528
527
|
|
|
@@ -531,14 +530,14 @@ class SettingsManager {
|
|
|
531
530
|
}
|
|
532
531
|
|
|
533
532
|
if (!allAuth[appUrl]) {
|
|
534
|
-
|
|
533
|
+
log.debug(`File contains no authentication data for app URL ${appUrl}`);
|
|
535
534
|
return new ParamCollection();
|
|
536
535
|
}
|
|
537
536
|
|
|
538
537
|
const authForSubdomain = allAuth[appUrl][subdomain];
|
|
539
538
|
|
|
540
539
|
if (!authForSubdomain) {
|
|
541
|
-
|
|
540
|
+
log.debug(`File contains no authentication data for subdomain ${subdomain} (app URL: ${appUrl})`);
|
|
542
541
|
return new ParamCollection();
|
|
543
542
|
}
|
|
544
543
|
|
|
@@ -557,7 +556,7 @@ class SettingsManager {
|
|
|
557
556
|
const authString = await keytar.getPassword(MTX_FULLY_QUALIFIED, getKeyringAccountName(new ParamCollection({ appUrl, subdomain })));
|
|
558
557
|
|
|
559
558
|
if (!authString) {
|
|
560
|
-
|
|
559
|
+
log.debug(`${capitalize(TOKEN_STORAGE_DESC.keyring)} contains no authentication data for URL ${appUrl} and subdomain '${subdomain}'`);
|
|
561
560
|
return new ParamCollection();
|
|
562
561
|
}
|
|
563
562
|
|
|
@@ -221,7 +221,8 @@ module.exports.csdl2openapi = function (
|
|
|
221
221
|
Object.keys(terms).forEach(vocab => {
|
|
222
222
|
voc[vocab] = {};
|
|
223
223
|
terms[vocab].forEach(term => {
|
|
224
|
-
|
|
224
|
+
if (alias['Org.OData.' + vocab + '.V1'] != undefined)
|
|
225
|
+
voc[vocab][term] = '@' + alias['Org.OData.' + vocab + '.V1'] + '.' + term;
|
|
225
226
|
});
|
|
226
227
|
});
|
|
227
228
|
|
|
@@ -1785,7 +1786,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
|
|
|
1785
1786
|
* @param {object} type Structured type
|
|
1786
1787
|
* @return {object} Map of Schemas Objects
|
|
1787
1788
|
*/
|
|
1788
|
-
|
|
1789
|
+
function schemasForStructuredType(schemas, qualifier, name, type, suffix) {
|
|
1789
1790
|
const schemaName = qualifier + "." + name + suffix;
|
|
1790
1791
|
const baseName = qualifier + "." + name;
|
|
1791
1792
|
const isKey = keyMap(type);
|
|
@@ -1796,18 +1797,19 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
|
|
|
1796
1797
|
Object.keys(properties).forEach(iName => {
|
|
1797
1798
|
const property = properties[iName];
|
|
1798
1799
|
if (suffix === SUFFIX.read) schemaProperties[iName] = getSchema(property);
|
|
1799
|
-
if((Object.prototype.hasOwnProperty.call(property, '@Common.FieldControl')) && property['@Common.FieldControl'] === 'Mandatory')
|
|
1800
|
-
{ required.push(iName) }
|
|
1800
|
+
if ((Object.prototype.hasOwnProperty.call(property, '@Common.FieldControl')) && property['@Common.FieldControl'] === 'Mandatory') { required.push(iName) }
|
|
1801
1801
|
if (property.$Kind == 'NavigationProperty') {
|
|
1802
1802
|
if (property.$Collection && suffix === "") {
|
|
1803
1803
|
schemaProperties[`${iName}@${csdl.$Version === '4.0' ? 'odata.' : ''}count`] = ref('count');
|
|
1804
1804
|
}
|
|
1805
|
-
if (suffix === SUFFIX.create)
|
|
1806
|
-
schemaProperties[iName] = getSchema(property, SUFFIX.create);
|
|
1807
|
-
if (suffix === SUFFIX.update && deepUpdate)
|
|
1808
|
-
schemaProperties[iName] = getSchema(property, SUFFIX.create);
|
|
1809
|
-
} else {
|
|
1810
1805
|
if (property[voc.Core.Permissions] != "Read" && !property[voc.Core.Computed] && property.$ContainsTarget) {
|
|
1806
|
+
if (suffix === SUFFIX.create)
|
|
1807
|
+
schemaProperties[iName] = getSchema(property, SUFFIX.create);
|
|
1808
|
+
if (suffix === SUFFIX.update && deepUpdate)
|
|
1809
|
+
schemaProperties[iName] = getSchema(property, SUFFIX.create);
|
|
1810
|
+
}
|
|
1811
|
+
} else {
|
|
1812
|
+
if (property[voc.Core.Permissions] === "Read" || property[voc.Core.Computed]) {
|
|
1811
1813
|
let index = required.indexOf(iName);
|
|
1812
1814
|
if (index != -1) required.splice(index, 1);
|
|
1813
1815
|
} else {
|
|
@@ -1838,7 +1840,7 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
|
|
|
1838
1840
|
if (derivedTypes[baseName]) {
|
|
1839
1841
|
schemas[schemaName].anyOf = [];
|
|
1840
1842
|
derivedTypes[baseName].forEach((derivedType) => {
|
|
1841
|
-
|
|
1843
|
+
schemas[schemaName].anyOf.push(ref(derivedType, suffix));
|
|
1842
1844
|
});
|
|
1843
1845
|
if (!type.$Abstract) schemas[schemaName].anyOf.push({});
|
|
1844
1846
|
}
|
|
@@ -2186,13 +2188,17 @@ see [Expand](http://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-prot
|
|
|
2186
2188
|
|
|
2187
2189
|
if (element[voc.Validation.Maximum] != undefined) {
|
|
2188
2190
|
if (s.$ref) s = { allOf: [s] };
|
|
2189
|
-
s.anyOf
|
|
2191
|
+
if (s.anyOf) {
|
|
2192
|
+
s.anyOf[0].maximum = element[voc.Validation.Maximum];
|
|
2193
|
+
}
|
|
2190
2194
|
if (element[voc.Validation.Maximum + voc.Validation.Exclusive]) s.exclusiveMaximum = true;
|
|
2191
2195
|
}
|
|
2192
2196
|
|
|
2193
2197
|
if (element[voc.Validation.Minimum] != undefined) {
|
|
2194
2198
|
if (s.$ref) s = { allOf: [s] };
|
|
2195
|
-
s.anyOf
|
|
2199
|
+
if (s.anyOf) {
|
|
2200
|
+
s.anyOf[0].minimum = element[voc.Validation.Minimum];
|
|
2201
|
+
}
|
|
2196
2202
|
if (element[voc.Validation.Minimum + voc.Validation.Exclusive]) s.exclusiveMinimum = true;
|
|
2197
2203
|
}
|
|
2198
2204
|
|
package/lib/import/message.js
CHANGED
|
@@ -13,7 +13,7 @@ const messages = {
|
|
|
13
13
|
INVALID_AS_OPTION: 'For --as option, please specify one of these output file format [cds, csn, json].',
|
|
14
14
|
INCORRECT_EXTENSION: 'If --as option is not provided, then please provide either .json or .csn output file extension.',
|
|
15
15
|
INCORRECT_INPUT_NAMESPACES: 'Invalid value for --include-namespaces option. Specify namespaces as comma-separated list or "*"',
|
|
16
|
-
|
|
16
|
+
MISSING_ASSOCIATION: 'Association is missing in the input file provided. Please check the edmx file.',
|
|
17
17
|
COLLECTION_IN_KEY: 'Key Property of an Entity cannot be of the type Collection.',
|
|
18
18
|
INVALID_OPENAPI_FILE: 'The OpenAPI file is not valid. Specify the correct OpenAPI file.'
|
|
19
19
|
};
|
|
@@ -57,7 +57,7 @@ class MetadataConverter {
|
|
|
57
57
|
* @memberof MetadataConverter
|
|
58
58
|
* @private
|
|
59
59
|
*/
|
|
60
|
-
|
|
60
|
+
_filterProperties(input, filter) {
|
|
61
61
|
const target = {};
|
|
62
62
|
Object
|
|
63
63
|
.keys(input)
|
|
@@ -466,7 +466,7 @@ class Expression {
|
|
|
466
466
|
*/
|
|
467
467
|
if (args.includes('$Scale') && (target.$Type === 'Edm.Decimal' || target.$UnderlyingType === 'Edm.Decimal')) {
|
|
468
468
|
if (attribs.Scale != null) {
|
|
469
|
-
if (attribs.Scale === 'variable' || attribs.Scale === 'floating'
|
|
469
|
+
if (attribs.Scale === 'variable' || attribs.Scale === 'floating') {
|
|
470
470
|
target.$Scale = attribs.Scale;
|
|
471
471
|
} else if (attribs.Scale !== '0') {
|
|
472
472
|
target.$Scale = parseInt(attribs.Scale, 10);
|
|
@@ -573,16 +573,16 @@ class Expression {
|
|
|
573
573
|
!(target.$Kind === 'NavigationProperty' && target.$Collection === true)) {
|
|
574
574
|
target.$Nullable = true;
|
|
575
575
|
}
|
|
576
|
-
this.includeExternalAnnotations(element,target);
|
|
576
|
+
this.includeExternalAnnotations(element, target);
|
|
577
577
|
}
|
|
578
|
-
includeExternalAnnotations(element,target){
|
|
578
|
+
includeExternalAnnotations(element, target) {
|
|
579
579
|
const nonStdAttributes = this.getNonStdAttributes(element.attributes);
|
|
580
580
|
for (let i = 0; i < nonStdAttributes.length; i++) {
|
|
581
581
|
let attr = nonStdAttributes[i];
|
|
582
582
|
let check = 0;
|
|
583
583
|
let [prefix, attrName] = attr.split(':');
|
|
584
584
|
if (attrName)
|
|
585
|
-
attrName = attrName.replace(/-/g,'.');
|
|
585
|
+
attrName = attrName.replace(/-/g, '.');
|
|
586
586
|
let attribute;
|
|
587
587
|
if ((MetadataConverter.custom_namespace.includes(prefix) || MetadataConverter.custom_namespace.includes(true)) && attrName !== undefined)
|
|
588
588
|
attribute = "@" + prefix + "." + attrName;
|
|
@@ -590,12 +590,12 @@ class Expression {
|
|
|
590
590
|
attribute = "$" + prefix;
|
|
591
591
|
if (attribute !== undefined && attribute.startsWith('$') === false) {
|
|
592
592
|
Object.entries(target).forEach(a => {
|
|
593
|
-
if (Object.keys(a[1]).length === 0 && typeof(a[1]) != 'boolean'
|
|
593
|
+
if (Object.keys(a[1]).length === 0 && typeof (a[1]) != 'boolean') {
|
|
594
594
|
a[1][attribute] = element.attributes[attr];
|
|
595
595
|
check = 1;
|
|
596
596
|
}
|
|
597
597
|
});
|
|
598
|
-
if (check == 0
|
|
598
|
+
if (check == 0)
|
|
599
599
|
target[attribute] = element.attributes[attr];
|
|
600
600
|
}
|
|
601
601
|
}
|
|
@@ -603,8 +603,8 @@ class Expression {
|
|
|
603
603
|
|
|
604
604
|
getNonStdAttributes(attributes) {
|
|
605
605
|
const attributeList = [];
|
|
606
|
-
const standardArrtibutes = ['MaxLength', 'Precision', 'Scale', 'Nullable', 'DefaultValue',
|
|
607
|
-
'Name', 'Type', 'HasStream', 'IsFlags', 'Alias', 'Namespace', 'BaseType', 'Partner',
|
|
606
|
+
const standardArrtibutes = ['MaxLength', 'Precision', 'Scale', 'Nullable', 'DefaultValue',
|
|
607
|
+
'Name', 'Type', 'HasStream', 'IsFlags', 'Alias', 'Namespace', 'BaseType', 'Partner',
|
|
608
608
|
'IsComposable', 'ContainsTarget', 'IsBound', 'EntitySet', 'EntitySetPath', 'EntityType'];
|
|
609
609
|
attributeList.push(...Object.keys(attributes).filter(attribute => standardArrtibutes.indexOf(attribute) === -1));
|
|
610
610
|
return attributeList;
|
|
@@ -1677,10 +1677,10 @@ class PropertyValueExpression extends Expression {
|
|
|
1677
1677
|
this.setTarget(target);
|
|
1678
1678
|
this.buildTypeCollectionAndFacets('$Type');
|
|
1679
1679
|
|
|
1680
|
-
if (element.attributes.Path) target[name] = {"=": element.attributes.Path};
|
|
1681
|
-
if (element.attributes.PropertyPath) target[name] = {"=": element.attributes.PropertyPath};
|
|
1682
|
-
if (element.attributes.EnumMember) target[name] = {"#": element.attributes.EnumMember.substring(element.attributes.EnumMember.lastIndexOf('/')+1)};
|
|
1683
|
-
if (element.attributes.NavigationPropertyPath) target[name] = {"=": element.attributes.NavigationPropertyPath};
|
|
1680
|
+
if (element.attributes.Path) target[name] = { "=": element.attributes.Path };
|
|
1681
|
+
if (element.attributes.PropertyPath) target[name] = { "=": element.attributes.PropertyPath };
|
|
1682
|
+
if (element.attributes.EnumMember) target[name] = { "#": element.attributes.EnumMember.substring(element.attributes.EnumMember.lastIndexOf('/') + 1) };
|
|
1683
|
+
if (element.attributes.NavigationPropertyPath) target[name] = { "=": element.attributes.NavigationPropertyPath };
|
|
1684
1684
|
if (element.attributes.String) target[name] = element.attributes.String;
|
|
1685
1685
|
if (element.attributes.Bool) target[name] = element.attributes.Bool === 'true';
|
|
1686
1686
|
|
|
@@ -1918,10 +1918,10 @@ class SchemaExpression extends Expression {
|
|
|
1918
1918
|
this.getStack().forEach((expr) => {
|
|
1919
1919
|
if (['FunctionExpression', 'ActionExpression'].indexOf(expr.getType()) > -1) {
|
|
1920
1920
|
const operationName = expr.getTargetPropertyName();
|
|
1921
|
-
if (target[operationName]
|
|
1921
|
+
if (target[operationName] && Array.isArray(target[operationName])) {
|
|
1922
1922
|
const operation = expr.interpret()[operationName];
|
|
1923
1923
|
target[operationName].push(...operation);
|
|
1924
|
-
} else {
|
|
1924
|
+
} else if (!target[operationName]) {
|
|
1925
1925
|
Object.assign(target, expr.interpret());
|
|
1926
1926
|
}
|
|
1927
1927
|
} else if (expr.getType() === 'AnnotationsExpression') {
|
|
@@ -2236,10 +2236,10 @@ class EntityContainerExpression extends Expression {
|
|
|
2236
2236
|
interpret() {
|
|
2237
2237
|
const element = this.getElement();
|
|
2238
2238
|
const target = { $Kind: 'EntityContainer' };
|
|
2239
|
-
this.includeExternalAnnotations(element,target);
|
|
2239
|
+
this.includeExternalAnnotations(element, target);
|
|
2240
2240
|
this.setTarget(target);
|
|
2241
2241
|
super.interpret();
|
|
2242
|
-
return { [element.attributes.Name]: target};
|
|
2242
|
+
return { [element.attributes.Name]: target };
|
|
2243
2243
|
}
|
|
2244
2244
|
}
|
|
2245
2245
|
|
|
@@ -2254,6 +2254,12 @@ class EntitySetExpression extends Expression {
|
|
|
2254
2254
|
if (this._kind === 'EntitySet') {
|
|
2255
2255
|
target.$Collection = true;
|
|
2256
2256
|
}
|
|
2257
|
+
if (this._kind === 'Singleton') {
|
|
2258
|
+
target.$Singleton = true;
|
|
2259
|
+
if (element.attributes.Nullable === 'true') {
|
|
2260
|
+
target.$SingletonNullable = true;
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2257
2263
|
this.setTarget(target);
|
|
2258
2264
|
this.buildTypeCollectionAndFacets('$Type');
|
|
2259
2265
|
|