@sap/cli-core 2024.9.0 → 2024.13.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 +95 -0
- package/cache/secrets/SecretsStorageImpl.d.ts +2 -0
- package/cache/secrets/SecretsStorageImpl.js +37 -12
- package/cache/secrets/utils.d.ts +2 -0
- package/cache/secrets/utils.js +9 -1
- package/commands/config.command/cache.command/clean.command.js +16 -3
- package/commands/config.command/index.js +25 -2
- package/commands/config.command/secrets.command/index.js +2 -1
- package/commands/config.command/secrets.command/refresh.command.d.ts +3 -0
- package/commands/config.command/secrets.command/refresh.command.js +13 -0
- package/commands/handler/authentication/oauth/tokenProvider/getToken.js +1 -0
- package/commands/handler/authentication/oauth/tokenProvider/utils.js +66 -58
- package/commands/handler/authentication/oauth/utils.js +8 -0
- package/commands/handler/authentication/technicalJWT/index.js +1 -1
- package/commands/handler/authentication/technicalJWT/utils.js +1 -1
- package/commands/handler/fetch/utils.d.ts +6 -1
- package/commands/handler/fetch/utils.js +60 -8
- package/commands/handler/input/file.js +4 -5
- package/commands/handler/input/input.js +5 -7
- package/commands/handler/next.js +1 -1
- package/commands/handler/options/utils.js +1 -1
- package/commands/handler/or.js +1 -1
- package/commands/handler/root/index.d.ts +2 -2
- package/commands/handler/root/index.js +22 -2
- package/commands/login.command.js +1 -1
- package/commands/openAPI.command/index.js +3 -2
- package/commands/openAPI.command/utils.d.ts +2 -1
- package/commands/openAPI.command/utils.js +22 -9
- package/constants.d.ts +2 -1
- package/constants.js +3 -24
- package/discovery/index.d.ts +1 -1
- package/discovery/index.js +7 -3
- package/dwc/dwc.js +5 -5
- package/dwc/run.js +1 -1
- package/dwc/utils.d.ts +1 -1
- package/dwc/utils.js +4 -4
- package/index.d.ts +3 -3
- package/package.json +5 -5
- package/schemas/discovery.json +13 -1
- package/types.d.ts +21 -1
- package/types.js +21 -1
- package/utils/commands.d.ts +4 -4
- package/utils/commands.js +64 -29
- package/utils/http/index.js +5 -5
- package/utils/utils.d.ts +2 -1
- package/utils/utils.js +2 -2
|
@@ -3,7 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.buildHttpConfig = exports.configRequiresBody = exports.handleResponse = exports.handleResponseData = exports.getOutputFileName = exports.buildParameters = exports.removeLeadingPathSegmentForPasscode = exports.checkConfiguration = void 0;
|
|
6
|
+
exports.buildHttpConfig = exports.configRequiresBody = exports.handleResponse = exports.handleResponseHeaders = exports.handleResponseData = exports.checkResponseDataForInvalidLoginData = exports.getOutputFileName = exports.buildParameters = exports.handleParameterMapping = exports.getValueFromMappping = exports.handleMappingIn = exports.removeLeadingPathSegmentForPasscode = exports.checkConfiguration = void 0;
|
|
7
|
+
const os_1 = require("os");
|
|
7
8
|
const url_1 = require("url");
|
|
8
9
|
const lodash_1 = __importDefault(require("lodash"));
|
|
9
10
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
@@ -59,8 +60,6 @@ const handleMappingIn = (mapping, url, value, headers, bodyWrapper) => {
|
|
|
59
60
|
url.pathname = url.pathname.replace(new RegExp(`${CURLY_OPEN_ENCODED}${mapping.name}${CURLY_CLOSED_ENCODED}`, "g"), value);
|
|
60
61
|
}
|
|
61
62
|
else {
|
|
62
|
-
// NOSONAR
|
|
63
|
-
// if (mapping.in === "body") {
|
|
64
63
|
if (!bodyWrapper.body) {
|
|
65
64
|
// eslint-disable-next-line no-param-reassign
|
|
66
65
|
bodyWrapper.body = {};
|
|
@@ -68,6 +67,7 @@ const handleMappingIn = (mapping, url, value, headers, bodyWrapper) => {
|
|
|
68
67
|
lodash_1.default.set(bodyWrapper.body, mapping.name, value);
|
|
69
68
|
}
|
|
70
69
|
};
|
|
70
|
+
exports.handleMappingIn = handleMappingIn;
|
|
71
71
|
const getValueFromMappping = (mapping, config) => {
|
|
72
72
|
if (mapping.source.type === "value") {
|
|
73
73
|
return mapping.source.value;
|
|
@@ -81,13 +81,15 @@ const getValueFromMappping = (mapping, config) => {
|
|
|
81
81
|
// mapping.source.type === "option"
|
|
82
82
|
return config.options[mapping.source.name];
|
|
83
83
|
};
|
|
84
|
+
exports.getValueFromMappping = getValueFromMappping;
|
|
84
85
|
const handleParameterMapping = (config, url, headers, bodyWrapper) => (mapping) => {
|
|
85
|
-
const value = getValueFromMappping(mapping, config);
|
|
86
|
+
const value = (0, exports.getValueFromMappping)(mapping, config);
|
|
86
87
|
if (value === undefined || value === null) {
|
|
87
88
|
return;
|
|
88
89
|
}
|
|
89
|
-
handleMappingIn(mapping, url, value, headers, bodyWrapper);
|
|
90
|
+
(0, exports.handleMappingIn)(mapping, url, value, headers, bodyWrapper);
|
|
90
91
|
};
|
|
92
|
+
exports.handleParameterMapping = handleParameterMapping;
|
|
91
93
|
const buildParameters = (path, parameterMappings) => {
|
|
92
94
|
const config = (0, config_1.get)();
|
|
93
95
|
const headers = {};
|
|
@@ -95,7 +97,7 @@ const buildParameters = (path, parameterMappings) => {
|
|
|
95
97
|
body: config.data,
|
|
96
98
|
};
|
|
97
99
|
const url = getUrl(path);
|
|
98
|
-
parameterMappings?.forEach(handleParameterMapping(config, url, headers, bodyWrapper));
|
|
100
|
+
parameterMappings?.forEach((0, exports.handleParameterMapping)(config, url, headers, bodyWrapper));
|
|
99
101
|
return {
|
|
100
102
|
url,
|
|
101
103
|
headers,
|
|
@@ -118,7 +120,21 @@ function getOutputFileName(outputPath = "") {
|
|
|
118
120
|
return config.options[constants_1.OPTION_OUTPUT.longName];
|
|
119
121
|
}
|
|
120
122
|
exports.getOutputFileName = getOutputFileName;
|
|
123
|
+
function checkResponseDataForInvalidLoginData(response) {
|
|
124
|
+
if (
|
|
125
|
+
// jira.tools.sap/browse/DW101-73607
|
|
126
|
+
typeof response === "string" &&
|
|
127
|
+
response.includes("Please contact your system administrator and ensure you have an active account on this system.")) {
|
|
128
|
+
const logger = (0, logger_1.get)("checkResponseDataForInvalidLoginData");
|
|
129
|
+
logger.output("WARNING: You are using an access token which is not known to the tenant. Please check your login credentials.");
|
|
130
|
+
logger.debug(response);
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
exports.checkResponseDataForInvalidLoginData = checkResponseDataForInvalidLoginData;
|
|
121
136
|
const handleResponseData = async (data, outputPath) => {
|
|
137
|
+
checkResponseDataForInvalidLoginData(data);
|
|
122
138
|
const config = (0, config_1.get)();
|
|
123
139
|
if (!config.doNotStoreResult) {
|
|
124
140
|
ResultHandlerFactory_1.ResultHandlerFactory.get().setResult(data);
|
|
@@ -139,12 +155,48 @@ const handleResponseData = async (data, outputPath) => {
|
|
|
139
155
|
}
|
|
140
156
|
};
|
|
141
157
|
exports.handleResponseData = handleResponseData;
|
|
158
|
+
function handleResponseHeaders(headers) {
|
|
159
|
+
const { error, output } = (0, logger_1.get)("handler.fetch.handleResponseHeaders");
|
|
160
|
+
if (headers[constants_1.X_DSP_API_DEPRECATED_PROPERTIES]) {
|
|
161
|
+
try {
|
|
162
|
+
const deprecatedProperties = JSON.parse(headers[constants_1.X_DSP_API_DEPRECATED_PROPERTIES]);
|
|
163
|
+
const deprecatedPropertiesStr = deprecatedProperties
|
|
164
|
+
.map((dp) => {
|
|
165
|
+
let msg = dp.name;
|
|
166
|
+
if (dp.deprecatedWithWave) {
|
|
167
|
+
msg += `. Deprecated since version ${dp.deprecatedWithWave}`;
|
|
168
|
+
}
|
|
169
|
+
if (dp.decommissionedAfterWave) {
|
|
170
|
+
msg += `. Decommissioned after version ${dp.decommissionedAfterWave}`;
|
|
171
|
+
}
|
|
172
|
+
if (dp.sapHelpUrl) {
|
|
173
|
+
msg += `. See the SAP Help documentation at ${dp.sapHelpUrl} for more information`;
|
|
174
|
+
}
|
|
175
|
+
if (dp.customMessage) {
|
|
176
|
+
msg += `. ${dp.customMessage}`;
|
|
177
|
+
}
|
|
178
|
+
return msg;
|
|
179
|
+
})
|
|
180
|
+
.reduce((prev, curr) => `${prev}${os_1.EOL}\t${curr}`, "");
|
|
181
|
+
output(`WARNING: the following properties are deprecated:${deprecatedPropertiesStr}`);
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
error(`failed to parse deprecated properties via header ${constants_1.X_DSP_API_DEPRECATED_PROPERTIES}`, err);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
exports.handleResponseHeaders = handleResponseHeaders;
|
|
142
189
|
const handleResponse = async (data, headers) => {
|
|
143
190
|
if (headers?.[constants_1.X_CSRF_TOKEN]) {
|
|
144
191
|
(0, config_1.set)({ [constants_1.X_CSRF_TOKEN]: headers[constants_1.X_CSRF_TOKEN] });
|
|
145
192
|
}
|
|
146
|
-
else
|
|
147
|
-
|
|
193
|
+
else {
|
|
194
|
+
if (data) {
|
|
195
|
+
await (0, exports.handleResponseData)(data, headers?.[constants_1.X_OUTPUT_FILE_NAME]);
|
|
196
|
+
}
|
|
197
|
+
if (headers) {
|
|
198
|
+
handleResponseHeaders(headers);
|
|
199
|
+
}
|
|
148
200
|
}
|
|
149
201
|
};
|
|
150
202
|
exports.handleResponse = handleResponse;
|
|
@@ -11,10 +11,12 @@ const constants_1 = require("../../../constants");
|
|
|
11
11
|
const checkOptionsExistence_1 = require("../checkOptionsExistence");
|
|
12
12
|
const next_1 = require("../next");
|
|
13
13
|
const options_1 = require("../options");
|
|
14
|
+
const utils_1 = require("../../../logger/utils");
|
|
14
15
|
const readBodyFromFile = async () => async () => {
|
|
15
16
|
const config = (0, config_1.get)();
|
|
16
17
|
const filePath = config.options[constants_1.OPTION_FILE_PATH.longName];
|
|
17
|
-
const
|
|
18
|
+
const logger = (0, logger_1.get)("commands.handler.input.file");
|
|
19
|
+
const { debug, trace } = logger;
|
|
18
20
|
debug("reading request body from %s", filePath);
|
|
19
21
|
try {
|
|
20
22
|
(0, config_1.set)({
|
|
@@ -22,10 +24,7 @@ const readBodyFromFile = async () => async () => {
|
|
|
22
24
|
});
|
|
23
25
|
}
|
|
24
26
|
catch (err) {
|
|
25
|
-
|
|
26
|
-
// eslint-disable-next-line
|
|
27
|
-
console.log(`Failed to read content from file ${filePath}. Does the file really exist and does it contain valid JSON?`);
|
|
28
|
-
}
|
|
27
|
+
(0, utils_1.logVerbose)(logger, `Failed to read content from file ${filePath}. Does the file really exist and does it contain valid JSON?`);
|
|
29
28
|
trace(`Failed to read content from file ${filePath}`, err);
|
|
30
29
|
throw err;
|
|
31
30
|
}
|
|
@@ -9,11 +9,12 @@ const config_1 = require("../../../config");
|
|
|
9
9
|
const constants_1 = require("../../../constants");
|
|
10
10
|
const logger_1 = require("../../../logger");
|
|
11
11
|
const commands_1 = require("../../../utils/commands");
|
|
12
|
+
const utils_1 = require("../../../logger/utils");
|
|
12
13
|
const create = () => async (command) => {
|
|
13
|
-
command.addOption(await (0, commands_1.buildOption)(command
|
|
14
|
+
command.addOption(await (0, commands_1.buildOption)(command, constants_1.OPTION_INPUT));
|
|
14
15
|
return async () => {
|
|
15
16
|
const config = (0, config_1.get)();
|
|
16
|
-
const
|
|
17
|
+
const logger = (0, logger_1.get)("handler.input.input");
|
|
17
18
|
if (config.options[constants_1.OPTION_FILE_PATH.longName]) {
|
|
18
19
|
throw new Error(`input provided through option --${constants_1.OPTION_FILE_PATH.longName}`);
|
|
19
20
|
}
|
|
@@ -28,15 +29,12 @@ const create = () => async (command) => {
|
|
|
28
29
|
if (!input) {
|
|
29
30
|
throw new Error("no input");
|
|
30
31
|
}
|
|
31
|
-
debug("reading request body from data");
|
|
32
|
+
logger.debug("reading request body from data");
|
|
32
33
|
try {
|
|
33
34
|
(0, config_1.set)({ data: JSON.parse(input) });
|
|
34
35
|
}
|
|
35
36
|
catch (err) {
|
|
36
|
-
|
|
37
|
-
// eslint-disable-next-line
|
|
38
|
-
console.log("Failed to parse input data. Is it a valid escaped JSON string?");
|
|
39
|
-
}
|
|
37
|
+
(0, utils_1.logVerbose)(logger, "Failed to parse input data. Is it a valid escaped JSON string?");
|
|
40
38
|
throw err;
|
|
41
39
|
}
|
|
42
40
|
};
|
package/commands/handler/next.js
CHANGED
|
@@ -16,7 +16,7 @@ const nextHandler = (origin, ...handlers) => async (command) => {
|
|
|
16
16
|
return async (...args) => {
|
|
17
17
|
getLogger(origin).trace(`next:${origin}: processing handlers`);
|
|
18
18
|
for (const handler of commandHandlers) {
|
|
19
|
-
// eslint-disable-next-line
|
|
19
|
+
// eslint-disable-next-line no-await-in-loop
|
|
20
20
|
await handler(...args);
|
|
21
21
|
}
|
|
22
22
|
};
|
|
@@ -15,7 +15,7 @@ const checkOptions = async (options, command) => {
|
|
|
15
15
|
if (!(0, commands_1.isOptionAlreadyRegistered)(option, command)) {
|
|
16
16
|
command.addOption(
|
|
17
17
|
// eslint-disable-next-line no-await-in-loop
|
|
18
|
-
await (0, commands_1.buildOption)(command
|
|
18
|
+
await (0, commands_1.buildOption)(command, { ...option, required: false }));
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
};
|
package/commands/handler/or.js
CHANGED
|
@@ -22,7 +22,7 @@ const create = (origin, ...handlers) => {
|
|
|
22
22
|
for (const handler of commandHandlers) {
|
|
23
23
|
handlerFailed = false;
|
|
24
24
|
try {
|
|
25
|
-
// eslint-disable-next-line
|
|
25
|
+
// eslint-disable-next-line no-await-in-loop
|
|
26
26
|
await handler(...args);
|
|
27
27
|
trace(`handler succeeded, exiting loop`);
|
|
28
28
|
break;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { Handler } from "../../../types";
|
|
2
|
-
export declare const create: (
|
|
1
|
+
import { Handler, LeafCommand } from "../../../types";
|
|
2
|
+
export declare const create: (command: LeafCommand, errorMessage?: string) => Handler;
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.create = void 0;
|
|
4
4
|
const logger_1 = require("../../../logger");
|
|
5
|
-
const create = (
|
|
5
|
+
const create = (command, errorMessage = "Command failed") => async (cCommand) => {
|
|
6
6
|
const { output } = (0, logger_1.get)("handler.root");
|
|
7
|
-
const hdlr = await handler(
|
|
7
|
+
const hdlr = await command.handler(cCommand);
|
|
8
8
|
return async (...args) => {
|
|
9
9
|
try {
|
|
10
10
|
await hdlr(...args);
|
|
@@ -13,6 +13,26 @@ const create = (handler, errorMessage = "Command failed") => async (command) =>
|
|
|
13
13
|
output(errorMessage);
|
|
14
14
|
throw err;
|
|
15
15
|
}
|
|
16
|
+
finally {
|
|
17
|
+
if (command.deprecationInfo?.deprecated) {
|
|
18
|
+
let message = `WARNING: The command '${command.command}' is deprecated.`;
|
|
19
|
+
if (command.deprecationInfo.deprecatedWithWave) {
|
|
20
|
+
message =
|
|
21
|
+
`WARNING: The command '${command.command}' is deprecated from version ` +
|
|
22
|
+
`${command.deprecationInfo.deprecatedWithWave} onwards.`;
|
|
23
|
+
}
|
|
24
|
+
if (command.deprecationInfo.decommissionedAfterWave) {
|
|
25
|
+
message += ` It will be removed after version ${command.deprecationInfo.decommissionedAfterWave}.`;
|
|
26
|
+
}
|
|
27
|
+
if (command.deprecationInfo.newCommand) {
|
|
28
|
+
message += ` Please start using the new command '${command.deprecationInfo.newCommand}'.`;
|
|
29
|
+
}
|
|
30
|
+
if (command.deprecationInfo.sapHelpUrl) {
|
|
31
|
+
message += ` See ${command.deprecationInfo.sapHelpUrl} for more information.`;
|
|
32
|
+
}
|
|
33
|
+
output(message);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
16
36
|
};
|
|
17
37
|
};
|
|
18
38
|
exports.create = create;
|
|
@@ -33,7 +33,7 @@ const initializeCache = async () => async () => {
|
|
|
33
33
|
await (await (0, init_command_1.init)())();
|
|
34
34
|
}
|
|
35
35
|
catch (err) {
|
|
36
|
-
warn(`option ${(0, utils_3.buildOptionName)(constants_1.
|
|
36
|
+
warn(`option ${(0, utils_3.buildOptionName)(constants_1.ROOT_COMMAND, constants_1.OPTION_HOST)} not defined, skipping cache init`);
|
|
37
37
|
}
|
|
38
38
|
};
|
|
39
39
|
const loginCommand = {
|
|
@@ -12,7 +12,7 @@ const getCommandDescription = (document, segments, index) => {
|
|
|
12
12
|
const segment = segments
|
|
13
13
|
.slice(0, index + 1)
|
|
14
14
|
.reduce((prev, curr) => (prev !== "" ? `${prev} ${curr}` : curr), "");
|
|
15
|
-
return document.tags.find((t) => t.name === segment)?.description
|
|
15
|
+
return document.tags.find((t) => t.name === segment)?.description ?? "";
|
|
16
16
|
};
|
|
17
17
|
const addCommandToArray = (document, commands, segments, index) => {
|
|
18
18
|
const segment = segments[index];
|
|
@@ -44,7 +44,7 @@ const addCommandToArray = (document, commands, segments, index) => {
|
|
|
44
44
|
return { command, remove: () => commands.splice(commands.length - 1, 1) };
|
|
45
45
|
};
|
|
46
46
|
const setHandler = (command, method, path, parameterMappings, readPathResponseHandler, ...handler) => {
|
|
47
|
-
// eslint-disable-next-line
|
|
47
|
+
// eslint-disable-next-line no-param-reassign
|
|
48
48
|
command.handler = (0, handler_1.createNextHandler)("openAPI.command", (0, handler_1.createParseArgumentsHandler)(), (0, handler_1.createMandatoryOptionsHandler)(), (0, handler_1.createAuthenticationHandler)(), ...handler, (0, handler_1.createFetchHandler)(method, path, parameterMappings, readPathResponseHandler));
|
|
49
49
|
};
|
|
50
50
|
const addCommands = async (program) => {
|
|
@@ -68,6 +68,7 @@ const addCommands = async (program) => {
|
|
|
68
68
|
(0, utils_1.handleForceOption)(operation, handler);
|
|
69
69
|
(0, utils_1.handleResponses)(operation, command);
|
|
70
70
|
(0, utils_1.handleParameters)(operation, doc, parameterMappings, command, (0, utils_1.getSchema)(pathItem.parameters || [], doc));
|
|
71
|
+
(0, utils_1.handleDeprecationNotice)(operation, command);
|
|
71
72
|
const readPathResponseHandler = (0, utils_1.handleReadPathHandler)(doc, operation);
|
|
72
73
|
setHandler(command, method, (0, utils_1.updatePath)(doc, path), parameterMappings, readPathResponseHandler, ...[(0, handler_1.createOptionsHandler)(command.options), ...handler]);
|
|
73
74
|
delete command.options;
|
|
@@ -11,9 +11,10 @@ export declare const handleReadPathHandler: (doc: Discovery, operation: Operatio
|
|
|
11
11
|
export declare const getDescriptionForCommand: (command: Command, operation: Operation) => string;
|
|
12
12
|
export declare const getSchema: <T>(obj: JSONReference | T, doc: Discovery) => T;
|
|
13
13
|
export declare const addOptionToCommand: (option: Option, options: Array<Option>) => void;
|
|
14
|
-
export declare const buildOptionFromType: (doc: Discovery, parameterIn: In, name: string, type: JSONSchema & ExtendedType, parameterMappings: ParameterMappings, options: Array<Option>, description?: string) => void;
|
|
14
|
+
export declare const buildOptionFromType: (commandName: string, doc: Discovery, parameterIn: In, name: string, type: JSONSchema & ExtendedType, parameterMappings: ParameterMappings, options: Array<Option>, description?: string) => void;
|
|
15
15
|
export declare const handleRequestBody: (operation: Operation, handler: Array<Handler>, doc: Discovery, parameterMappings: ParameterMappings, command: Command) => void;
|
|
16
16
|
export declare const handleForceOption: (operation: Operation, handler: Array<Handler>) => void;
|
|
17
17
|
export declare const handleResponses: (operation: Operation, command: Command) => void;
|
|
18
18
|
export declare const handleParameters: (operation: Operation, doc: Discovery, parameterMappings: ParameterMappings, command: Command, topLevelParameters?: Parameters) => void;
|
|
19
|
+
export declare const handleDeprecationNotice: (operation: Operation, command: Command) => void;
|
|
19
20
|
export {};
|
|
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.handleParameters = exports.handleResponses = exports.handleForceOption = exports.handleRequestBody = exports.buildOptionFromType = exports.addOptionToCommand = exports.getSchema = exports.getDescriptionForCommand = exports.handleReadPathHandler = exports.getSegments = exports.writeParameter = exports.isPathParameter = exports.updatePath = void 0;
|
|
26
|
+
exports.handleDeprecationNotice = exports.handleParameters = exports.handleResponses = exports.handleForceOption = exports.handleRequestBody = exports.buildOptionFromType = exports.addOptionToCommand = exports.getSchema = exports.getDescriptionForCommand = exports.handleReadPathHandler = exports.getSegments = exports.writeParameter = exports.isPathParameter = exports.updatePath = void 0;
|
|
27
27
|
const lodash_1 = __importStar(require("lodash"));
|
|
28
28
|
const constants_1 = require("../../constants");
|
|
29
29
|
const logger_1 = require("../../logger");
|
|
@@ -221,7 +221,7 @@ const handleOptionName = (name, params, description) => {
|
|
|
221
221
|
description: newDescription,
|
|
222
222
|
};
|
|
223
223
|
};
|
|
224
|
-
const buildOption = (longName, types, params, description, def) => {
|
|
224
|
+
const buildOption = (commandName, longName, types, params, description, def) => {
|
|
225
225
|
const option = {
|
|
226
226
|
longName,
|
|
227
227
|
required: !types[0].allowEmptyValue && types[0].required,
|
|
@@ -246,13 +246,13 @@ const buildOption = (longName, types, params, description, def) => {
|
|
|
246
246
|
}
|
|
247
247
|
return option;
|
|
248
248
|
};
|
|
249
|
-
const handleOption = (name, params, types, options, description) => {
|
|
249
|
+
const handleOption = (commandName, name, params, types, options, description) => {
|
|
250
250
|
const { optionName, def, description: newDescription, } = handleOptionName(name, params, description);
|
|
251
|
-
const option = buildOption(optionName, types, params, newDescription, def);
|
|
251
|
+
const option = buildOption(commandName, optionName, types, params, newDescription, def);
|
|
252
252
|
(0, exports.addOptionToCommand)(option, options);
|
|
253
253
|
};
|
|
254
254
|
const requiresUserInput = (name) => name !== constants_1.X_CSRF_TOKEN;
|
|
255
|
-
const buildOptionFromType = (doc, parameterIn, name, type, parameterMappings, options, description) => {
|
|
255
|
+
const buildOptionFromType = (commandName, doc, parameterIn, name, type, parameterMappings, options, description) => {
|
|
256
256
|
try {
|
|
257
257
|
const types = flattenType(type, doc);
|
|
258
258
|
checkTypes(types);
|
|
@@ -266,7 +266,7 @@ const buildOptionFromType = (doc, parameterIn, name, type, parameterMappings, op
|
|
|
266
266
|
else {
|
|
267
267
|
const params = initParams(types);
|
|
268
268
|
if (requiresUserInput(name)) {
|
|
269
|
-
handleOption(name, params, types, options, description);
|
|
269
|
+
handleOption(commandName, name, params, types, options, description);
|
|
270
270
|
}
|
|
271
271
|
parameterMappings.push({
|
|
272
272
|
in: parameterIn,
|
|
@@ -301,7 +301,7 @@ const handleRequestBody = (operation, handler, doc, parameterMappings, command)
|
|
|
301
301
|
if (key.schema.oneOf) {
|
|
302
302
|
throw new Error("invalid request body parameter resolution, oneOf not supported");
|
|
303
303
|
}
|
|
304
|
-
(0, exports.buildOptionFromType)(doc, "body", key.key, {
|
|
304
|
+
(0, exports.buildOptionFromType)(command.command, doc, "body", key.key, {
|
|
305
305
|
...key.schema,
|
|
306
306
|
required: key.required,
|
|
307
307
|
}, parameterMappings, command.options, key.schema.description);
|
|
@@ -327,15 +327,28 @@ const handleParameters = (operation, doc, parameterMappings, command, topLevelPa
|
|
|
327
327
|
(operation.parameters || []).concat(topLevelParameters).forEach((p) => {
|
|
328
328
|
try {
|
|
329
329
|
const parameter = (0, exports.getSchema)(p, doc);
|
|
330
|
-
(0, exports.buildOptionFromType)(doc, parameter.in, parameter.name, {
|
|
330
|
+
(0, exports.buildOptionFromType)(command.command, doc, parameter.in, parameter.name, {
|
|
331
331
|
...parameter.schema,
|
|
332
332
|
allowEmptyValue: parameter.allowEmptyValue,
|
|
333
333
|
required: parameter.required,
|
|
334
334
|
}, parameterMappings, command.options, parameter.description);
|
|
335
335
|
}
|
|
336
336
|
catch (err) {
|
|
337
|
-
error(
|
|
337
|
+
error(`cannot add option ${p} for operation ${operation.operationId}`, err.stack);
|
|
338
338
|
}
|
|
339
339
|
});
|
|
340
340
|
};
|
|
341
341
|
exports.handleParameters = handleParameters;
|
|
342
|
+
const handleDeprecationNotice = (operation, command) => {
|
|
343
|
+
if (operation.deprecated && command.type === "command") {
|
|
344
|
+
// eslint-disable-next-line no-param-reassign
|
|
345
|
+
command.deprecationInfo = {
|
|
346
|
+
deprecated: true,
|
|
347
|
+
deprecatedWithWave: operation["x-deprecated-with-wave"],
|
|
348
|
+
decommissionedAfterWave: operation["x-decommissioned-after-wave"],
|
|
349
|
+
newCommand: operation["x-deprecation-new-command"],
|
|
350
|
+
sapHelpUrl: operation["x-deprecation-sap-help-url"],
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
exports.handleDeprecationNotice = handleDeprecationNotice;
|
package/constants.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export declare enum AuthenticationMethod {
|
|
|
17
17
|
oauth = "oauth",
|
|
18
18
|
passcode = "passcode"
|
|
19
19
|
}
|
|
20
|
-
export declare const
|
|
20
|
+
export declare const ROOT_COMMAND: any;
|
|
21
21
|
export declare const CLI_NAME = "cli-name";
|
|
22
22
|
export declare const CLI_PACKAGE_NAME = "cli-package-name";
|
|
23
23
|
export declare const CLI_DESCRIPTION = "cli-description";
|
|
@@ -31,6 +31,7 @@ export declare const CLI_GENERIC_OPTIONS_HELP = "cli-generic-options-help";
|
|
|
31
31
|
export declare const SEGMENTS_TO_REMOVE_FOR_PASSCODE_AUTH: string[];
|
|
32
32
|
export declare const DISCOVERY_METADATA_PATH = "discovery-metadata.json";
|
|
33
33
|
export declare const X_CSRF_TOKEN = "x-csrf-token";
|
|
34
|
+
export declare const X_DSP_API_DEPRECATED_PROPERTIES = "x-dsp-api-deprecated-properties";
|
|
34
35
|
export declare const X_OUTPUT_FILE_NAME = "x-sap-datasphere-cli-file-name";
|
|
35
36
|
export declare const PATH_TO_SUCCESS_HTML: string;
|
|
36
37
|
export declare const PATH_TO_ERROR_HTML: string;
|
package/constants.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.OPTION_BROWSER = exports.OPTION_INPUT = exports.OPTION_FILE_PATH = exports.OPTION_OPTIONS_FILE = exports.CONFIG_PASSCODE_FUNCTION = exports.OPTION_PASSCODE = exports.OPTION_CODE = exports.OPTION_SECRETS_FILE = exports.OPTION_EXPIRES_IN = exports.OPTION_REFRESH_TOKEN = exports.OPTION_ACCESS_TOKEN = exports.OPTION_TOKEN_URL = exports.OPTION_AUTHORIZATION_URL = exports.OPTION_CLIENT_SECRET = exports.OPTION_CLIENT_ID = exports.OPTION_FORCE = exports.OPTION_VERBOSE = exports.OPTION_NO_PRETTY = exports.OPTION_OUTPUT = exports.OPTION_LOGIN_ID = exports.OPTION_HOST = exports.OPTION_HELP = exports.OPTION_VERSION = exports.CACHE_SECRETS_FILE = exports.PATH_TO_ERROR_HTML = exports.PATH_TO_SUCCESS_HTML = exports.X_OUTPUT_FILE_NAME = exports.X_CSRF_TOKEN = exports.DISCOVERY_METADATA_PATH = exports.SEGMENTS_TO_REMOVE_FOR_PASSCODE_AUTH = exports.CLI_GENERIC_OPTIONS_HELP = exports.CLI_SUPPORTED_AUTHENTICATION_METHODS = exports.CLI_DEPRECATION_MESSAGE = exports.CLI_DEPRECATED = exports.CLI_VERSION = exports.CLI_SAP_HELP = exports.CLI_DISCOVERY_PATHS = exports.CLI_DESCRIPTION = exports.CLI_PACKAGE_NAME = exports.CLI_NAME = exports.
|
|
6
|
+
exports.OPTION_BROWSER = exports.OPTION_INPUT = exports.OPTION_FILE_PATH = exports.OPTION_OPTIONS_FILE = exports.CONFIG_PASSCODE_FUNCTION = exports.OPTION_PASSCODE = exports.OPTION_CODE = exports.OPTION_SECRETS_FILE = exports.OPTION_EXPIRES_IN = exports.OPTION_REFRESH_TOKEN = exports.OPTION_ACCESS_TOKEN = exports.OPTION_TOKEN_URL = exports.OPTION_AUTHORIZATION_URL = exports.OPTION_CLIENT_SECRET = exports.OPTION_CLIENT_ID = exports.OPTION_FORCE = exports.OPTION_VERBOSE = exports.OPTION_NO_PRETTY = exports.OPTION_OUTPUT = exports.OPTION_LOGIN_ID = exports.OPTION_HOST = exports.OPTION_HELP = exports.OPTION_VERSION = exports.CACHE_SECRETS_FILE = exports.PATH_TO_ERROR_HTML = exports.PATH_TO_SUCCESS_HTML = exports.X_OUTPUT_FILE_NAME = exports.X_DSP_API_DEPRECATED_PROPERTIES = exports.X_CSRF_TOKEN = exports.DISCOVERY_METADATA_PATH = exports.SEGMENTS_TO_REMOVE_FOR_PASSCODE_AUTH = exports.CLI_GENERIC_OPTIONS_HELP = exports.CLI_SUPPORTED_AUTHENTICATION_METHODS = exports.CLI_DEPRECATION_MESSAGE = exports.CLI_DEPRECATED = exports.CLI_VERSION = exports.CLI_SAP_HELP = exports.CLI_DISCOVERY_PATHS = exports.CLI_DESCRIPTION = exports.CLI_PACKAGE_NAME = exports.CLI_NAME = exports.ROOT_COMMAND = exports.AuthenticationMethod = exports.DISCOVERY_DOCUMENT_PREFIX = exports.VERSION = void 0;
|
|
7
7
|
const path_1 = __importDefault(require("path"));
|
|
8
8
|
const utils_1 = require("./utils/utils");
|
|
9
9
|
exports.VERSION = (0, utils_1.getVersion)();
|
|
@@ -13,7 +13,7 @@ var AuthenticationMethod;
|
|
|
13
13
|
AuthenticationMethod["oauth"] = "oauth";
|
|
14
14
|
AuthenticationMethod["passcode"] = "passcode";
|
|
15
15
|
})(AuthenticationMethod || (exports.AuthenticationMethod = AuthenticationMethod = {}));
|
|
16
|
-
exports.
|
|
16
|
+
exports.ROOT_COMMAND = { name: () => "ROOT", parent: null };
|
|
17
17
|
exports.CLI_NAME = "cli-name";
|
|
18
18
|
exports.CLI_PACKAGE_NAME = "cli-package-name";
|
|
19
19
|
exports.CLI_DESCRIPTION = "cli-description";
|
|
@@ -27,25 +27,23 @@ exports.CLI_GENERIC_OPTIONS_HELP = "cli-generic-options-help";
|
|
|
27
27
|
exports.SEGMENTS_TO_REMOVE_FOR_PASSCODE_AUTH = ["dwaas-core"];
|
|
28
28
|
exports.DISCOVERY_METADATA_PATH = "discovery-metadata.json";
|
|
29
29
|
exports.X_CSRF_TOKEN = "x-csrf-token";
|
|
30
|
+
exports.X_DSP_API_DEPRECATED_PROPERTIES = "x-dsp-api-deprecated-properties";
|
|
30
31
|
exports.X_OUTPUT_FILE_NAME = "x-sap-datasphere-cli-file-name";
|
|
31
32
|
exports.PATH_TO_SUCCESS_HTML = path_1.default.join(__dirname, "assets", "success.html");
|
|
32
33
|
exports.PATH_TO_ERROR_HTML = path_1.default.join(__dirname, "assets", "error.html");
|
|
33
34
|
exports.CACHE_SECRETS_FILE = "secrets.json";
|
|
34
35
|
exports.OPTION_VERSION = {
|
|
35
|
-
commandName: exports.ROOT_COMMAND_NAME,
|
|
36
36
|
longName: "version",
|
|
37
37
|
description: "print version",
|
|
38
38
|
hidden: true,
|
|
39
39
|
};
|
|
40
40
|
exports.OPTION_HELP = {
|
|
41
|
-
commandName: exports.ROOT_COMMAND_NAME,
|
|
42
41
|
longName: "help",
|
|
43
42
|
description: "print help for a command",
|
|
44
43
|
args: [{ name: "command" }],
|
|
45
44
|
hidden: true,
|
|
46
45
|
};
|
|
47
46
|
exports.OPTION_HOST = {
|
|
48
|
-
commandName: exports.ROOT_COMMAND_NAME,
|
|
49
47
|
longName: "host",
|
|
50
48
|
description: "specifies the url where the tenant is hosted",
|
|
51
49
|
args: [{ name: "host" }],
|
|
@@ -56,7 +54,6 @@ exports.OPTION_HOST = {
|
|
|
56
54
|
},
|
|
57
55
|
};
|
|
58
56
|
exports.OPTION_LOGIN_ID = {
|
|
59
|
-
commandName: exports.ROOT_COMMAND_NAME,
|
|
60
57
|
longName: "login-id",
|
|
61
58
|
description: "specifies the login ID",
|
|
62
59
|
args: [{ name: "id" }],
|
|
@@ -68,31 +65,26 @@ exports.OPTION_LOGIN_ID = {
|
|
|
68
65
|
},
|
|
69
66
|
};
|
|
70
67
|
exports.OPTION_OUTPUT = {
|
|
71
|
-
commandName: exports.ROOT_COMMAND_NAME,
|
|
72
68
|
longName: "output",
|
|
73
69
|
description: "specifies the file to store the output of the command",
|
|
74
70
|
args: [{ name: "output", optional: true }],
|
|
75
71
|
hidden: true,
|
|
76
72
|
};
|
|
77
73
|
exports.OPTION_NO_PRETTY = {
|
|
78
|
-
commandName: exports.ROOT_COMMAND_NAME,
|
|
79
74
|
longName: "no-pretty",
|
|
80
75
|
description: "do not pretty-format JSON responses",
|
|
81
76
|
hidden: true,
|
|
82
77
|
};
|
|
83
78
|
exports.OPTION_VERBOSE = {
|
|
84
|
-
commandName: exports.ROOT_COMMAND_NAME,
|
|
85
79
|
longName: "verbose",
|
|
86
80
|
description: "print detailed log information to console",
|
|
87
81
|
hidden: true,
|
|
88
82
|
};
|
|
89
83
|
exports.OPTION_FORCE = {
|
|
90
|
-
commandName: exports.ROOT_COMMAND_NAME,
|
|
91
84
|
longName: "force",
|
|
92
85
|
description: "force the command execution",
|
|
93
86
|
};
|
|
94
87
|
exports.OPTION_CLIENT_ID = {
|
|
95
|
-
commandName: exports.ROOT_COMMAND_NAME,
|
|
96
88
|
longName: "client-id",
|
|
97
89
|
description: "client id for interactive oauth session authentication",
|
|
98
90
|
args: [{ name: "id" }],
|
|
@@ -103,7 +95,6 @@ exports.OPTION_CLIENT_ID = {
|
|
|
103
95
|
},
|
|
104
96
|
};
|
|
105
97
|
exports.OPTION_CLIENT_SECRET = {
|
|
106
|
-
commandName: exports.ROOT_COMMAND_NAME,
|
|
107
98
|
longName: "client-secret",
|
|
108
99
|
description: "client secret for interactive oauth session authentication",
|
|
109
100
|
args: [{ name: "secret" }],
|
|
@@ -114,7 +105,6 @@ exports.OPTION_CLIENT_SECRET = {
|
|
|
114
105
|
},
|
|
115
106
|
};
|
|
116
107
|
exports.OPTION_AUTHORIZATION_URL = {
|
|
117
|
-
commandName: exports.ROOT_COMMAND_NAME,
|
|
118
108
|
longName: "authorization-url",
|
|
119
109
|
description: "authorization url for interactive oauth session authentication",
|
|
120
110
|
args: [{ name: "url" }],
|
|
@@ -125,7 +115,6 @@ exports.OPTION_AUTHORIZATION_URL = {
|
|
|
125
115
|
},
|
|
126
116
|
};
|
|
127
117
|
exports.OPTION_TOKEN_URL = {
|
|
128
|
-
commandName: exports.ROOT_COMMAND_NAME,
|
|
129
118
|
longName: "token-url",
|
|
130
119
|
description: "token url for interactive oauth session authentication",
|
|
131
120
|
args: [{ name: "url" }],
|
|
@@ -136,7 +125,6 @@ exports.OPTION_TOKEN_URL = {
|
|
|
136
125
|
},
|
|
137
126
|
};
|
|
138
127
|
exports.OPTION_ACCESS_TOKEN = {
|
|
139
|
-
commandName: exports.ROOT_COMMAND_NAME,
|
|
140
128
|
longName: "access-token",
|
|
141
129
|
description: "access token for interactive oauth session authentication",
|
|
142
130
|
args: [{ name: "token" }],
|
|
@@ -147,7 +135,6 @@ exports.OPTION_ACCESS_TOKEN = {
|
|
|
147
135
|
},
|
|
148
136
|
};
|
|
149
137
|
exports.OPTION_REFRESH_TOKEN = {
|
|
150
|
-
commandName: exports.ROOT_COMMAND_NAME,
|
|
151
138
|
longName: "refresh-token",
|
|
152
139
|
description: "refresh token for interactive oauth session authentication",
|
|
153
140
|
args: [{ name: "token" }],
|
|
@@ -158,7 +145,6 @@ exports.OPTION_REFRESH_TOKEN = {
|
|
|
158
145
|
},
|
|
159
146
|
};
|
|
160
147
|
exports.OPTION_EXPIRES_IN = {
|
|
161
|
-
commandName: exports.ROOT_COMMAND_NAME,
|
|
162
148
|
longName: "expires-in",
|
|
163
149
|
description: "expires in information for interactive oauth session authentication",
|
|
164
150
|
args: [{ name: "expires" }],
|
|
@@ -169,7 +155,6 @@ exports.OPTION_EXPIRES_IN = {
|
|
|
169
155
|
},
|
|
170
156
|
};
|
|
171
157
|
exports.OPTION_SECRETS_FILE = {
|
|
172
|
-
commandName: exports.ROOT_COMMAND_NAME,
|
|
173
158
|
longName: "secrets-file",
|
|
174
159
|
description: "path to secrets file",
|
|
175
160
|
args: [{ name: "file" }],
|
|
@@ -180,7 +165,6 @@ exports.OPTION_SECRETS_FILE = {
|
|
|
180
165
|
},
|
|
181
166
|
};
|
|
182
167
|
exports.OPTION_CODE = {
|
|
183
|
-
commandName: exports.ROOT_COMMAND_NAME,
|
|
184
168
|
longName: "code",
|
|
185
169
|
description: "code for oauth token retrieval",
|
|
186
170
|
args: [{ name: "code" }],
|
|
@@ -191,7 +175,6 @@ exports.OPTION_CODE = {
|
|
|
191
175
|
},
|
|
192
176
|
};
|
|
193
177
|
exports.OPTION_PASSCODE = {
|
|
194
|
-
commandName: exports.ROOT_COMMAND_NAME,
|
|
195
178
|
longName: "passcode",
|
|
196
179
|
description: "passcode for interactive session authentication",
|
|
197
180
|
args: [{ name: "passcode" }],
|
|
@@ -199,14 +182,12 @@ exports.OPTION_PASSCODE = {
|
|
|
199
182
|
};
|
|
200
183
|
exports.CONFIG_PASSCODE_FUNCTION = "passcodeFunction";
|
|
201
184
|
exports.OPTION_OPTIONS_FILE = {
|
|
202
|
-
commandName: exports.ROOT_COMMAND_NAME,
|
|
203
185
|
longName: "options-file",
|
|
204
186
|
description: "path to options file",
|
|
205
187
|
args: [{ name: "file" }],
|
|
206
188
|
hidden: true,
|
|
207
189
|
};
|
|
208
190
|
exports.OPTION_FILE_PATH = {
|
|
209
|
-
commandName: exports.ROOT_COMMAND_NAME,
|
|
210
191
|
longName: "file-path",
|
|
211
192
|
description: "specifies the file to use as input for the command",
|
|
212
193
|
args: [{ name: "path" }],
|
|
@@ -216,7 +197,6 @@ exports.OPTION_FILE_PATH = {
|
|
|
216
197
|
},
|
|
217
198
|
};
|
|
218
199
|
exports.OPTION_INPUT = {
|
|
219
|
-
commandName: exports.ROOT_COMMAND_NAME,
|
|
220
200
|
longName: "input",
|
|
221
201
|
description: "specifies input as string to use for the command",
|
|
222
202
|
args: [{ name: "input" }],
|
|
@@ -226,7 +206,6 @@ exports.OPTION_INPUT = {
|
|
|
226
206
|
},
|
|
227
207
|
};
|
|
228
208
|
exports.OPTION_BROWSER = {
|
|
229
|
-
commandName: exports.ROOT_COMMAND_NAME,
|
|
230
209
|
longName: "browser",
|
|
231
210
|
description: "specifies the browser to open",
|
|
232
211
|
args: [{ name: "browser" }],
|
package/discovery/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Discovery, DiscoveryMetadata } from "../types";
|
|
2
2
|
export declare const getPathToDiscoveryDocument: () => string;
|
|
3
3
|
export declare const getMetadata: () => Promise<Array<DiscoveryMetadata>>;
|
|
4
|
-
export declare const addMetadata: ({ tenant, addedAt, }: DiscoveryMetadata) => Promise<void>;
|
|
4
|
+
export declare const addMetadata: ({ tenant, addedAt, }: Omit<DiscoveryMetadata, "hash">) => Promise<void>;
|
|
5
5
|
export declare const clear: () => void;
|
|
6
6
|
export declare const init: () => Promise<Discovery>;
|
|
7
7
|
export declare const compareEtags: () => Promise<boolean>;
|
package/discovery/index.js
CHANGED
|
@@ -17,11 +17,14 @@ const getLogger = () => (0, logger_1.get)("discovery");
|
|
|
17
17
|
let initialized = false;
|
|
18
18
|
let document;
|
|
19
19
|
let metadata;
|
|
20
|
+
function getHash(tenant) {
|
|
21
|
+
return (0, utils_2.sha256)(`${tenant}${JSON.stringify((0, core_1.getDiscoveryPaths)())}`).replace(/[/\\]/g, "_");
|
|
22
|
+
}
|
|
20
23
|
const getDocumentName = () => {
|
|
21
24
|
const { trace } = getLogger();
|
|
22
25
|
const config = (0, config_1.get)();
|
|
23
|
-
const
|
|
24
|
-
const name = `${constants_1.DISCOVERY_DOCUMENT_PREFIX}${
|
|
26
|
+
const hash = getHash(config.publicfqdn);
|
|
27
|
+
const name = `${constants_1.DISCOVERY_DOCUMENT_PREFIX}${hash}.json`;
|
|
25
28
|
trace(`calculating document name for host ${config.host}, name ${name}`);
|
|
26
29
|
return name;
|
|
27
30
|
};
|
|
@@ -47,7 +50,8 @@ exports.getMetadata = getMetadata;
|
|
|
47
50
|
const addMetadata = async ({ tenant, addedAt, }) => {
|
|
48
51
|
await initMetadata();
|
|
49
52
|
metadata = metadata.filter((t) => t.tenant !== tenant);
|
|
50
|
-
|
|
53
|
+
const hash = getHash(tenant);
|
|
54
|
+
metadata.push({ tenant, addedAt, hash });
|
|
51
55
|
await (0, cache_1.writeFile)(constants_1.DISCOVERY_METADATA_PATH, JSON.stringify(metadata));
|
|
52
56
|
};
|
|
53
57
|
exports.addMetadata = addMetadata;
|