@azure/api-management-custom-widgets-tools 1.0.0-beta.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Microsoft Corporation.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE
package/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # Tools for working with custom widgets of Developer portal of Azure API Management service
2
+
3
+ This repository is managed by the [Microsoft Azure API Management](https://aka.ms/apimrocks) team and contains the
4
+ source code of tools for development of custom widgets for the Developer portal. Refer to
5
+ the [official Azure documentation](https://aka.ms/apimdocs/portal/customwidgets) for more information and instructions.
6
+
7
+ [Source code](https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/apimanagement/api-management-custom-widgets-scaffolder/) |
8
+ [Package (NPM)](https://www.npmjs.com/package/@azure/api-management-custom-widgets-scaffolder) |
9
+ [Product documentation](https://aka.ms/apimdocs/portal/)
10
+
11
+ ## Getting started
12
+
13
+ ### Currently supported environments
14
+
15
+ - [LTS versions of Node.js](https://nodejs.org/about/releases/)
16
+ - Latest versions of Safari, Chrome, Edge, and Firefox
17
+
18
+ ### Prerequisites
19
+
20
+ - An [Azure Subscription](https://azure.microsoft.com)
21
+ - An [API Management](https://aka.ms/apimdocs/) resource
22
+ - Active [Developer portal](https://aka.ms/apimdocs/portal/)
23
+ - A project of a custom widget generated by the [`@azure/api-management-custom-widgets-scaffolder` package](https://www.npmjs.com/package/@azure/api-management-custom-widgets-scaffolder)
24
+
25
+ ### Create your first Custom widget
26
+
27
+ Navigate to [Custom widgets for Developer portal documentation](https://aka.ms/apimdocs/portal/customwidgets) to learn how to create and manage Custom widgets.
28
+
29
+ ## Key concepts
30
+
31
+ Package provides tools for communication between scaffolded widgets and the Developer Portal
32
+
33
+ This package should not be confused with `@azure/api-management-custom-widgets-scaffolder` package, which contains template files and a function to generate Custom widgets for the API Management services' Developer Portal.
34
+
35
+ ## Contributing
36
+
37
+ If you'd like to contribute to this library, please read the [contributing guide](https://github.com/Azure/azure-sdk-for-js/blob/main/CONTRIBUTING.md) to learn more about how to build and test the code.
38
+
39
+ To execute the tests you'll need to run:
40
+
41
+ 1. `rush update`
42
+ 2. `rush build -t @azure/api-management-custom-widgets-tools`
43
+ 3. `cd sdk\apimanagement\api-management-custom-widgets-tools`
44
+ 4. `npm run test`
45
+
46
+ View our [tests](https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/apimanagement/api-management-custom-widgets-scaffolder/test)
47
+ folder for more details.
48
+
49
+ ## Related projects
50
+
51
+ - [Microsoft Azure SDK for JavaScript](https://github.com/Azure/azure-sdk-for-js)
package/dist/index.js ADDED
@@ -0,0 +1,344 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var tslib = require('tslib');
6
+ var storageBlob = require('@azure/storage-blob');
7
+ var mime = require('mime');
8
+ var fs = require('fs');
9
+ var identity = require('@azure/identity');
10
+ var coreClient = require('@azure-rest/core-client');
11
+ var path = require('path');
12
+
13
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
14
+
15
+ var mime__default = /*#__PURE__*/_interopDefaultLegacy(mime);
16
+ var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
17
+ var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
18
+
19
+ // Copyright (c) Microsoft Corporation.
20
+ // Licensed under the MIT license.
21
+ /**
22
+ * Key for a search param, from which editor data will be loaded from.
23
+ */
24
+ const APIM_EDITOR_DATA_KEY = "editorData";
25
+ /**
26
+ * Key for a post message object, it's used to propagate changes from editor to the DevPortal. Used to prevent interference with other applications.
27
+ */
28
+ const APIM_ON_CHANGE_MESSAGE_KEY = "customInputValueChangedMSAPIM";
29
+ /**
30
+ * Key for a post message object, it's used to request and send secrets - token and user id, from the DevPortal. Used to prevent interference with other applications.
31
+ */
32
+ const APIM_ASK_FOR_SECRETS_MESSAGE_KEY = "askForSecretsMSAPIM";
33
+ function parseWidgetData(urlSearchParams) {
34
+ var _a;
35
+ try {
36
+ const urlEditorParams = JSON.parse(decodeURIComponent((_a = urlSearchParams.get(APIM_EDITOR_DATA_KEY)) !== null && _a !== void 0 ? _a : ""));
37
+ // if (!("origin" in urlEditorParams)) {
38
+ // console.error(
39
+ // "Could not get 'origin' from the search params of the URL:\n" + self.location.href
40
+ // );
41
+ // }
42
+ return urlEditorParams;
43
+ }
44
+ catch (e) {
45
+ console.error(`Could not get '${APIM_EDITOR_DATA_KEY}' from the search params of the URL:\n` +
46
+ self.location, e);
47
+ return { values: {}, environment: "error", instanceId: "error" };
48
+ }
49
+ }
50
+ function getWidgetDataPure(urlSearchParams) {
51
+ return parseWidgetData(urlSearchParams);
52
+ }
53
+ /**
54
+ * Function to get all data related to the widget including technical values not expected to be needed in most cases.
55
+ * Intended mostly for internal use, API might change. Consider using getValues or getEditorValues instead.
56
+ */
57
+ function getWidgetData() {
58
+ return getWidgetDataPure(new URLSearchParams(self.location.search));
59
+ }
60
+ function getEditorValuesPure(urlSearchParams) {
61
+ return getWidgetDataPure(urlSearchParams).values;
62
+ }
63
+ /**
64
+ * Function to get values you've set in the admin editor window.
65
+ */
66
+ function getEditorValues() {
67
+ return getEditorValuesPure(new URLSearchParams(self.location.search));
68
+ }
69
+ function getValuesPure(valuesDefault, urlSearchParams) {
70
+ const values = Object.assign({}, valuesDefault); // set Obj to contain all possible values and prefill default value
71
+ const urlValues = parseWidgetData(urlSearchParams).values;
72
+ Object.keys(values).forEach((key) => {
73
+ const value = urlValues[key];
74
+ if (value != null && value !== "")
75
+ values[key] = value; // if value is specified in the URL, replace the default value
76
+ });
77
+ return values;
78
+ }
79
+ /**
80
+ * Function to get values you've set in the admin editor window. Undefined/empty values are replaced with default values.
81
+ *
82
+ * @param valuesDefault - object with your default values to use, just import valuesDefault object from values.ts folder
83
+ */
84
+ function getValues(valuesDefault) {
85
+ return getValuesPure(valuesDefault, new URLSearchParams(self.location.search));
86
+ }
87
+ /**
88
+ * The onChange function itself with 'origin' provided as a param.
89
+ *
90
+ * @param origin - web content's origin (URL) of your Dev Portal to send changes to
91
+ * @param instanceId - ID of this particular instance of the widget
92
+ * @param values - values that changed
93
+ */
94
+ function onChangeWithOrigin(origin, instanceId, values) {
95
+ Object.entries(values).forEach(([key, value]) => {
96
+ self.parent.postMessage({ [APIM_ON_CHANGE_MESSAGE_KEY]: { key, value, instanceId } }, origin);
97
+ });
98
+ }
99
+ /**
100
+ * Build onChange function, which you can use, to send changed data from the editor.
101
+ */
102
+ function buildOnChange() {
103
+ const { instanceId } = getWidgetData();
104
+ return (values) => onChangeWithOrigin("*", instanceId, values);
105
+ }
106
+ /**
107
+ * Request secrets - token & userId, from the Dev portal parent window.
108
+ *
109
+ * @param targetModule - is the function invoke from the main "app" window or the admin "editor"?
110
+ */
111
+ async function askForSecrets(targetModule) {
112
+ let receiveSecrets;
113
+ const promise = new Promise((resolve, reject) => {
114
+ const { instanceId, environment } = getWidgetData();
115
+ receiveSecrets = ({ data }) => {
116
+ if (!(APIM_ASK_FOR_SECRETS_MESSAGE_KEY in data))
117
+ return;
118
+ const secrets = data[APIM_ASK_FOR_SECRETS_MESSAGE_KEY];
119
+ if (typeof secrets !== "object" || !("managementApiUrl" in secrets)) {
120
+ reject("Secrets send by Dev Portal are invalid");
121
+ }
122
+ resolve(secrets);
123
+ };
124
+ self.addEventListener("message", receiveSecrets);
125
+ const message = {
126
+ [APIM_ASK_FOR_SECRETS_MESSAGE_KEY]: {
127
+ instanceId,
128
+ origin: self.location.origin,
129
+ targetModule,
130
+ },
131
+ };
132
+ if (targetModule === "app" && environment === "development") {
133
+ self.parent.parent.postMessage(message, "*");
134
+ }
135
+ else {
136
+ self.parent.postMessage(message, "*");
137
+ }
138
+ });
139
+ return promise.finally(() => self.removeEventListener("message", receiveSecrets));
140
+ }
141
+
142
+ // Copyright (c) Microsoft Corporation.
143
+ // Licensed under the MIT license.
144
+ /** root of the blob storage folder */
145
+ const BLOB_ROOT = "custom-widgets";
146
+ /** name of the blob storage folder with widget implementation */
147
+ const BLOB_DATA_FOLDER = "data";
148
+ /** name of the blob storage folder with widget configs */
149
+ const BLOB_CONFIGS_FOLDER = "configs";
150
+ /** name of the configuration file */
151
+ const APIM_CONFIG_FILE_NAME = "config.msapim.json";
152
+ /**
153
+ * Generate relative path for widgets' data on the blob storage
154
+ *
155
+ * @param name - name of the widget
156
+ */
157
+ function buildBlobDataPath(name) {
158
+ return `${BLOB_ROOT}/${BLOB_DATA_FOLDER}/${name}/`;
159
+ }
160
+ /**
161
+ * Generate relative path for widgets' config on the blob storage
162
+ *
163
+ * @param name - name of the widget
164
+ */
165
+ function buildBlobConfigPath(name) {
166
+ return `${BLOB_ROOT}/${BLOB_CONFIGS_FOLDER}/${name}/${APIM_CONFIG_FILE_NAME}`;
167
+ }
168
+
169
+ // Copyright (c) Microsoft Corporation.
170
+ /**
171
+ * A service wrapping ContainerClient class to simplify blob handling
172
+ */
173
+ class CustomWidgetBlobService {
174
+ /**
175
+ * @param blobStorageUrl - blob storage SAS URL
176
+ * @param name - name of the custom widget to be taken care of
177
+ */
178
+ constructor(blobStorageUrl, name) {
179
+ const container = "content";
180
+ const blobServiceClient = new storageBlob.BlobServiceClient(blobStorageUrl.replace(`/${container}`, ""));
181
+ this.containerClient = blobServiceClient.getContainerClient(container);
182
+ this.name = name;
183
+ this.pathWidget = buildBlobDataPath(name);
184
+ this.pathConfig = buildBlobConfigPath(name);
185
+ }
186
+ extractFileName(path) {
187
+ return path.split("/").pop();
188
+ }
189
+ async blobUpload(absolutePath, content) {
190
+ const fileName = this.extractFileName(absolutePath);
191
+ if (!fileName)
192
+ throw new Error("a fileName was not found in the absolutePath");
193
+ return this.containerClient.getBlockBlobClient(absolutePath).upload(content, content.length, {
194
+ blobHTTPHeaders: { blobContentType: mime__default["default"].getType(fileName) || "application/octet-stream" },
195
+ });
196
+ }
197
+ async jsonUpload(absolutePath, json) {
198
+ return this.blobUpload(absolutePath, Buffer.from(JSON.stringify(json)));
199
+ }
200
+ async blobDownload(absolutePath) {
201
+ return this.containerClient.getBlockBlobClient(absolutePath).downloadToBuffer();
202
+ }
203
+ async dirDelete(absolutePath) {
204
+ var e_1, _a;
205
+ try {
206
+ for (var _b = tslib.__asyncValues(await this.containerClient.listBlobsFlat({ prefix: absolutePath })), _c; _c = await _b.next(), !_c.done;) {
207
+ const blob = _c.value;
208
+ await this.containerClient.deleteBlob(blob.name);
209
+ }
210
+ }
211
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
212
+ finally {
213
+ try {
214
+ if (_c && !_c.done && (_a = _b.return)) await _a.call(_b);
215
+ }
216
+ finally { if (e_1) throw e_1.error; }
217
+ }
218
+ }
219
+ async uploadWidgetDataFile(file, content) {
220
+ return this.blobUpload(this.pathWidget + file, content);
221
+ }
222
+ async cleanDataDir() {
223
+ return this.dirDelete(this.pathWidget);
224
+ }
225
+ async getConfig() {
226
+ const buffer = await this.blobDownload(this.pathConfig);
227
+ return JSON.parse(buffer.toString());
228
+ }
229
+ async uploadConfig(config) {
230
+ return this.jsonUpload(this.pathConfig, config);
231
+ }
232
+ }
233
+ var CustomWidgetBlobService$1 = CustomWidgetBlobService;
234
+
235
+ // Copyright (c) Microsoft Corporation.
236
+ async function getAccessToken(managementApiEndpoint) {
237
+ const credentials = new identity.InteractiveBrowserCredential();
238
+ const scope = `${managementApiEndpoint}/user_impersonation`;
239
+ const { token } = await credentials.getToken(scope);
240
+ return `Bearer ${token}`;
241
+ }
242
+ /**
243
+ * Function to get storage SAS URL.
244
+ *
245
+ * @returns storage SAS URL
246
+ */
247
+ async function getStorageSasUrl({ managementApiEndpoint, resourceId, apiVersion = "2019-01-01", tokenOverride, }) {
248
+ var _a;
249
+ const httpClient = coreClient.getClient(`${managementApiEndpoint}/${resourceId}`, { apiVersion });
250
+ const response = await httpClient
251
+ .pathUnchecked(`/portalSettings/mediaContent/listSecrets?apiVersion=${apiVersion}`) // TODO
252
+ .post({
253
+ headers: {
254
+ "If-Match": "*",
255
+ "Content-Type": "application/json",
256
+ Authorization: tokenOverride !== null && tokenOverride !== void 0 ? tokenOverride : (await getAccessToken(managementApiEndpoint)),
257
+ },
258
+ });
259
+ if (!((_a = response === null || response === void 0 ? void 0 : response.body) === null || _a === void 0 ? void 0 : _a.containerSasUrl))
260
+ throw new Error("Could not get storage SAS URL");
261
+ return response.body.containerSasUrl;
262
+ }
263
+
264
+ // Copyright (c) Microsoft Corporation.
265
+ function readdir(dir, root) {
266
+ const results = [];
267
+ fs__default["default"].readdirSync(root + dir).forEach((file) => {
268
+ const stat = fs__default["default"].statSync(root + dir + path__default["default"].sep + file);
269
+ if (stat && stat.isDirectory()) {
270
+ results.push(...readdir(dir + file + path__default["default"].sep, root));
271
+ }
272
+ else {
273
+ results.push(dir + file);
274
+ }
275
+ });
276
+ return results;
277
+ }
278
+
279
+ // Copyright (c) Microsoft Corporation.
280
+ /**
281
+ * Deploys everything from /dist folder to the API Management DevPortals' blob storage.
282
+ *
283
+ * @param serviceInformation - service information for deployment
284
+ * @param name - name of the widget to be deployed
285
+ * @param fallbackConfigPath - local path to the config file (by default "./static/config.msapim.json")
286
+ * @param rootLocal - optional, root of the local folder with compiled project to be exported (by default "./dist")
287
+ */
288
+ async function deploy(serviceInformation, name, fallbackConfigPath = "./static/" + APIM_CONFIG_FILE_NAME, rootLocal = "./dist/") {
289
+ console.log("\n\n");
290
+ console.log("Starting deploy process of custom widget: " + name);
291
+ console.log("Please, sign in to your Azure account when prompted\n");
292
+ const blobStorageUrl = await getStorageSasUrl(serviceInformation);
293
+ const customWidgetBlobService = new CustomWidgetBlobService$1(blobStorageUrl, name);
294
+ let config;
295
+ try {
296
+ console.log("Looking for config file in the Azure blob storage");
297
+ config = await customWidgetBlobService.getConfig();
298
+ }
299
+ catch (e) {
300
+ console.log("Config not found.");
301
+ }
302
+ if (!config) {
303
+ console.log("Looking for a local config file in: " + fallbackConfigPath);
304
+ config = JSON.parse(fs__default["default"].readFileSync(fallbackConfigPath).toString());
305
+ }
306
+ if (!config) {
307
+ throw new Error("Config file could not be loaded.");
308
+ }
309
+ console.log("Config file loaded\n");
310
+ const files = readdir("", rootLocal);
311
+ console.log("Starting upload of data files from the '" + rootLocal + "' folder\n");
312
+ await customWidgetBlobService.cleanDataDir();
313
+ const promises = [];
314
+ files.forEach((file) => {
315
+ const content = fs__default["default"].readFileSync(rootLocal + file);
316
+ const promise = customWidgetBlobService
317
+ .uploadWidgetDataFile(file, content)
318
+ .then(() => console.log("Uploaded file: " + file));
319
+ promises.push(promise);
320
+ });
321
+ await Promise.all(promises);
322
+ console.log(files.length + " files has been uploaded\n");
323
+ config.deployedOn = new Date();
324
+ await customWidgetBlobService.uploadConfig(config);
325
+ console.log("Uploaded updated config");
326
+ }
327
+
328
+ exports.APIM_ASK_FOR_SECRETS_MESSAGE_KEY = APIM_ASK_FOR_SECRETS_MESSAGE_KEY;
329
+ exports.APIM_CONFIG_FILE_NAME = APIM_CONFIG_FILE_NAME;
330
+ exports.APIM_EDITOR_DATA_KEY = APIM_EDITOR_DATA_KEY;
331
+ exports.APIM_ON_CHANGE_MESSAGE_KEY = APIM_ON_CHANGE_MESSAGE_KEY;
332
+ exports.BLOB_CONFIGS_FOLDER = BLOB_CONFIGS_FOLDER;
333
+ exports.BLOB_DATA_FOLDER = BLOB_DATA_FOLDER;
334
+ exports.BLOB_ROOT = BLOB_ROOT;
335
+ exports.askForSecrets = askForSecrets;
336
+ exports.buildBlobConfigPath = buildBlobConfigPath;
337
+ exports.buildBlobDataPath = buildBlobDataPath;
338
+ exports.buildOnChange = buildOnChange;
339
+ exports.deployNodeJS = deploy;
340
+ exports.getEditorValues = getEditorValues;
341
+ exports.getValues = getValues;
342
+ exports.getWidgetData = getWidgetData;
343
+ exports.onChangeWithOrigin = onChangeWithOrigin;
344
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/utils.ts","../src/paths.ts","../src/node/CustomWidgetBlobService.ts","../src/node/getStorageSasUrl.ts","../src/node/readdir.ts","../src/node/deploy.ts"],"sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\n/**\n * Key for a search param, from which editor data will be loaded from.\n */\nexport const APIM_EDITOR_DATA_KEY = \"editorData\";\n/**\n * Key for a post message object, it's used to propagate changes from editor to the DevPortal. Used to prevent interference with other applications.\n */\nexport const APIM_ON_CHANGE_MESSAGE_KEY = \"customInputValueChangedMSAPIM\";\n/**\n * Key for a post message object, it's used to request and send secrets - token and user id, from the DevPortal. Used to prevent interference with other applications.\n */\nexport const APIM_ASK_FOR_SECRETS_MESSAGE_KEY = \"askForSecretsMSAPIM\";\n\n/**\n * Base of a values obj\n */\nexport type ValuesCommon = Record<string, unknown>;\n/**\n * All possible runtime environments\n */\nexport type Environment = \"development\" | \"publishing\" | \"runtime\" | \"error\";\n\n/** Information about the widget instance received from the Dev Portal */\nexport interface PortalData {\n // /** web content's origin (URL) of your Dev Portal */\n // origin: string;\n /** current runtime environment */\n environment: Environment;\n /** ID of this particular instance of the widget */\n instanceId: string;\n}\n\n/** JSON object with all the data you'll receive from the Dev Portal */\nexport interface EditorData<Values extends ValuesCommon> extends PortalData {\n /** values you've set in the admin editor window */\n values: Partial<Values>;\n}\n\nfunction parseWidgetData<Values extends ValuesCommon>(\n urlSearchParams: URLSearchParams\n): EditorData<Values> {\n try {\n const urlEditorParams: EditorData<Values> = JSON.parse(\n decodeURIComponent(urlSearchParams.get(APIM_EDITOR_DATA_KEY) ?? \"\")\n );\n\n // if (!(\"origin\" in urlEditorParams)) {\n // console.error(\n // \"Could not get 'origin' from the search params of the URL:\\n\" + self.location.href\n // );\n // }\n return urlEditorParams;\n } catch (e) {\n console.error(\n `Could not get '${APIM_EDITOR_DATA_KEY}' from the search params of the URL:\\n` +\n self.location,\n e\n );\n return { values: {}, environment: \"error\", instanceId: \"error\" };\n }\n}\n\nexport function getWidgetDataPure<Values extends ValuesCommon>(\n urlSearchParams: URLSearchParams\n): EditorData<Values> {\n return parseWidgetData<Values>(urlSearchParams);\n}\n\n/**\n * Function to get all data related to the widget including technical values not expected to be needed in most cases.\n * Intended mostly for internal use, API might change. Consider using getValues or getEditorValues instead.\n */\nexport function getWidgetData<Values extends ValuesCommon>(): EditorData<Values> {\n return getWidgetDataPure(new URLSearchParams(self.location.search));\n}\n\nexport function getEditorValuesPure<Values extends ValuesCommon>(\n urlSearchParams: URLSearchParams\n): Partial<Values> {\n return getWidgetDataPure<Values>(urlSearchParams).values;\n}\n\n/**\n * Function to get values you've set in the admin editor window.\n */\nexport function getEditorValues<Values extends ValuesCommon>(): Partial<Values> {\n return getEditorValuesPure<Values>(new URLSearchParams(self.location.search));\n}\n\nexport function getValuesPure<Values extends ValuesCommon>(\n valuesDefault: Values,\n urlSearchParams: URLSearchParams\n): Values {\n const values = { ...valuesDefault }; // set Obj to contain all possible values and prefill default value\n const urlValues = parseWidgetData<Values>(urlSearchParams).values;\n\n Object.keys(values).forEach((key: keyof Values) => {\n const value = urlValues[key];\n if (value != null && value !== \"\") values[key] = value as Values[typeof key]; // if value is specified in the URL, replace the default value\n });\n return values;\n}\n\n/**\n * Function to get values you've set in the admin editor window. Undefined/empty values are replaced with default values.\n *\n * @param valuesDefault - object with your default values to use, just import valuesDefault object from values.ts folder\n */\nexport function getValues<Values extends ValuesCommon>(valuesDefault: Values): Values {\n return getValuesPure(valuesDefault, new URLSearchParams(self.location.search));\n}\n\n/**\n * Type of the onChange function.\n */\nexport type OnChange<Values extends ValuesCommon> = (values: Partial<Values>) => void;\n\n/**\n * The onChange function itself with 'origin' provided as a param.\n *\n * @param origin - web content's origin (URL) of your Dev Portal to send changes to\n * @param instanceId - ID of this particular instance of the widget\n * @param values - values that changed\n */\nexport function onChangeWithOrigin<Values extends ValuesCommon>(\n origin: string,\n instanceId: string,\n values: Values\n): void {\n Object.entries(values).forEach(([key, value]) => {\n self.parent.postMessage({ [APIM_ON_CHANGE_MESSAGE_KEY]: { key, value, instanceId } }, origin);\n });\n}\n\n/**\n * Build onChange function, which you can use, to send changed data from the editor.\n */\nexport function buildOnChange<Values extends ValuesCommon>(): OnChange<Values> {\n const { instanceId } = getWidgetData();\n return (values: Partial<Values>) => onChangeWithOrigin(\"*\", instanceId, values);\n}\n\n/**\n * Possible target modules\n * \"app\" for main application which is embedded in your Dev Portal\n * \"editor\" for form in admin panel\n */\nexport type TargetModule = \"app\" | \"editor\";\n/**\n * Secrets needed for communication with Dev Portal back-end\n */\nexport type Secrets = {\n managementApiUrl: string;\n apiVersion: string;\n userId?: string;\n token?: string;\n};\n\n/**\n * Request secrets - token & userId, from the Dev portal parent window.\n *\n * @param targetModule - is the function invoke from the main \"app\" window or the admin \"editor\"?\n */\nexport async function askForSecrets(targetModule: TargetModule): Promise<Secrets> {\n let receiveSecrets: (e: MessageEvent) => void;\n\n const promise = new Promise<Secrets>((resolve, reject) => {\n const { instanceId, environment }: PortalData = getWidgetData();\n\n receiveSecrets = ({ data }) => {\n if (!(APIM_ASK_FOR_SECRETS_MESSAGE_KEY in data)) return;\n\n const secrets = data[APIM_ASK_FOR_SECRETS_MESSAGE_KEY];\n if (typeof secrets !== \"object\" || !(\"managementApiUrl\" in secrets)) {\n reject(\"Secrets send by Dev Portal are invalid\");\n }\n\n resolve(secrets);\n };\n\n self.addEventListener(\"message\", receiveSecrets);\n\n const message = {\n [APIM_ASK_FOR_SECRETS_MESSAGE_KEY]: {\n instanceId,\n origin: self.location.origin,\n targetModule,\n },\n };\n\n if (targetModule === \"app\" && environment === \"development\") {\n self.parent.parent.postMessage(message, \"*\");\n } else {\n self.parent.postMessage(message, \"*\");\n }\n });\n\n return promise.finally(() => self.removeEventListener(\"message\", receiveSecrets));\n}\n","// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\n/** root of the blob storage folder */\nexport const BLOB_ROOT = \"custom-widgets\";\n/** name of the blob storage folder with widget implementation */\nexport const BLOB_DATA_FOLDER = \"data\";\n/** name of the blob storage folder with widget configs */\nexport const BLOB_CONFIGS_FOLDER = \"configs\";\n/** name of the configuration file */\nexport const APIM_CONFIG_FILE_NAME = \"config.msapim.json\";\n\n/**\n * Generate relative path for widgets' data on the blob storage\n *\n * @param name - name of the widget\n */\nexport function buildBlobDataPath(name: string): string {\n return `${BLOB_ROOT}/${BLOB_DATA_FOLDER}/${name}/`;\n}\n\n/**\n * Generate relative path for widgets' config on the blob storage\n *\n * @param name - name of the widget\n */\nexport function buildBlobConfigPath(name: string): string {\n return `${BLOB_ROOT}/${BLOB_CONFIGS_FOLDER}/${name}/${APIM_CONFIG_FILE_NAME}`;\n}\n","// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { BlobServiceClient, BlockBlobUploadResponse } from \"@azure/storage-blob\";\nimport { buildBlobConfigPath, buildBlobDataPath } from \"../paths\";\nimport mime from \"mime\";\n\nexport type Config = Record<string, unknown>;\n\n/**\n * A service wrapping ContainerClient class to simplify blob handling\n */\nexport class CustomWidgetBlobService {\n readonly containerClient;\n readonly name;\n readonly pathWidget;\n readonly pathConfig;\n\n /**\n * @param blobStorageUrl - blob storage SAS URL\n * @param name - name of the custom widget to be taken care of\n */\n constructor(blobStorageUrl: string, name: string) {\n const container = \"content\";\n const blobServiceClient = new BlobServiceClient(blobStorageUrl.replace(`/${container}`, \"\"));\n this.containerClient = blobServiceClient.getContainerClient(container);\n this.name = name;\n this.pathWidget = buildBlobDataPath(name);\n this.pathConfig = buildBlobConfigPath(name);\n }\n\n private extractFileName(path: string): string | undefined {\n return path.split(\"/\").pop();\n }\n\n async blobUpload(absolutePath: string, content: Buffer): Promise<BlockBlobUploadResponse> {\n const fileName = this.extractFileName(absolutePath);\n if (!fileName) throw new Error(\"a fileName was not found in the absolutePath\");\n return this.containerClient.getBlockBlobClient(absolutePath).upload(content, content.length, {\n blobHTTPHeaders: { blobContentType: mime.getType(fileName) || \"application/octet-stream\" },\n });\n }\n\n async jsonUpload(\n absolutePath: string,\n json: Record<string, unknown>\n ): Promise<BlockBlobUploadResponse> {\n return this.blobUpload(absolutePath, Buffer.from(JSON.stringify(json)));\n }\n\n async blobDownload(absolutePath: string): Promise<Buffer> {\n return this.containerClient.getBlockBlobClient(absolutePath).downloadToBuffer();\n }\n\n async dirDelete(absolutePath: string): Promise<void> {\n for await (const blob of await this.containerClient.listBlobsFlat({ prefix: absolutePath })) {\n await this.containerClient.deleteBlob(blob.name);\n }\n }\n\n async uploadWidgetDataFile(file: string, content: Buffer): Promise<BlockBlobUploadResponse> {\n return this.blobUpload(this.pathWidget + file, content);\n }\n\n async cleanDataDir(): Promise<void> {\n return this.dirDelete(this.pathWidget);\n }\n\n async getConfig(): Promise<Config> {\n const buffer = await this.blobDownload(this.pathConfig);\n return JSON.parse(buffer.toString());\n }\n\n async uploadConfig(config: Config): Promise<BlockBlobUploadResponse> {\n return this.jsonUpload(this.pathConfig, config);\n }\n}\n\nexport default CustomWidgetBlobService;\n","// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { InteractiveBrowserCredential } from \"@azure/identity\";\nimport { ServiceInformation } from \"./deploy\";\nimport { getClient } from \"@azure-rest/core-client\";\n\nasync function getAccessToken(managementApiEndpoint: string): Promise<string> {\n const credentials = new InteractiveBrowserCredential();\n const scope = `${managementApiEndpoint}/user_impersonation`;\n const { token } = await credentials.getToken(scope);\n return `Bearer ${token}`;\n}\n\n/**\n * Function to get storage SAS URL.\n *\n * @returns storage SAS URL\n */\nasync function getStorageSasUrl({\n managementApiEndpoint,\n resourceId,\n apiVersion = \"2019-01-01\",\n tokenOverride,\n}: ServiceInformation): Promise<string> {\n const httpClient = getClient(`${managementApiEndpoint}/${resourceId}`, { apiVersion });\n const response = await httpClient\n .pathUnchecked(`/portalSettings/mediaContent/listSecrets?apiVersion=${apiVersion}`) // TODO\n .post({\n headers: {\n \"If-Match\": \"*\",\n \"Content-Type\": \"application/json\",\n Authorization: tokenOverride ?? (await getAccessToken(managementApiEndpoint)),\n },\n });\n\n if (!response?.body?.containerSasUrl) throw new Error(\"Could not get storage SAS URL\");\n return response.body.containerSasUrl;\n}\n\nexport default getStorageSasUrl;\n","// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport fs from \"fs\";\nimport path from \"path\";\n\nfunction readdir(dir: string, root: string): string[] {\n const results: string[] = [];\n\n fs.readdirSync(root + dir).forEach((file) => {\n const stat = fs.statSync(root + dir + path.sep + file);\n if (stat && stat.isDirectory()) {\n results.push(...readdir(dir + file + path.sep, root));\n } else {\n results.push(dir + file);\n }\n });\n\n return results;\n}\n\nexport default readdir;\n","// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport CustomWidgetBlobService, { Config } from \"./CustomWidgetBlobService\";\nimport { APIM_CONFIG_FILE_NAME } from \"../paths\";\nimport fs from \"fs\";\nimport getStorageSasUrl from \"./getStorageSasUrl\";\nimport readdir from \"./readdir\";\n\n/**\n * resourceId - resource ID of API Management service \"subscriptions/[subscription-id]/resourceGroups/[resource-group-name]/providers/Microsoft.ApiManagement/service/[service-name]\"\n * managementApiEndpoint - URL with protocol (e.g. https://management.azure.com)\n * apiVersion - optional to override default (e.g. \"2019-01-01\")\n * tokenOverride - optional, provides token to use for auth, instead of 'az login' approach\n */\nexport type ServiceInformation = {\n resourceId: string;\n managementApiEndpoint: string;\n apiVersion?: string;\n tokenOverride?: string;\n};\n\n/**\n * Deploys everything from /dist folder to the API Management DevPortals' blob storage.\n *\n * @param serviceInformation - service information for deployment\n * @param name - name of the widget to be deployed\n * @param fallbackConfigPath - local path to the config file (by default \"./static/config.msapim.json\")\n * @param rootLocal - optional, root of the local folder with compiled project to be exported (by default \"./dist\")\n */\nasync function deploy(\n serviceInformation: ServiceInformation,\n name: string,\n fallbackConfigPath = \"./static/\" + APIM_CONFIG_FILE_NAME,\n rootLocal: string = \"./dist/\"\n): Promise<void> {\n console.log(\"\\n\\n\");\n console.log(\"Starting deploy process of custom widget: \" + name);\n console.log(\"Please, sign in to your Azure account when prompted\\n\");\n\n const blobStorageUrl = await getStorageSasUrl(serviceInformation);\n const customWidgetBlobService = new CustomWidgetBlobService(blobStorageUrl, name);\n\n let config: Config | undefined;\n try {\n console.log(\"Looking for config file in the Azure blob storage\");\n config = await customWidgetBlobService.getConfig();\n } catch (e) {\n console.log(\"Config not found.\");\n }\n if (!config) {\n console.log(\"Looking for a local config file in: \" + fallbackConfigPath);\n config = JSON.parse(fs.readFileSync(fallbackConfigPath).toString());\n }\n if (!config) {\n throw new Error(\"Config file could not be loaded.\");\n }\n\n console.log(\"Config file loaded\\n\");\n\n const files = readdir(\"\", rootLocal);\n\n console.log(\"Starting upload of data files from the '\" + rootLocal + \"' folder\\n\");\n\n await customWidgetBlobService.cleanDataDir();\n\n const promises: Promise<void>[] = [];\n files.forEach((file) => {\n const content = fs.readFileSync(rootLocal + file);\n const promise = customWidgetBlobService\n .uploadWidgetDataFile(file, content)\n .then(() => console.log(\"Uploaded file: \" + file));\n promises.push(promise);\n });\n await Promise.all(promises);\n\n console.log(files.length + \" files has been uploaded\\n\");\n\n config.deployedOn = new Date();\n await customWidgetBlobService.uploadConfig(config);\n console.log(\"Uploaded updated config\");\n}\n\nexport default deploy;\n"],"names":["BlobServiceClient","mime","__asyncValues","InteractiveBrowserCredential","getClient","fs","path","CustomWidgetBlobService"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT license.
3
+ /**
4
+ * @packageDocumentation https://aka.ms/apimdocs/portal/customwidgets
5
+ */
6
+ export { APIM_EDITOR_DATA_KEY, APIM_ON_CHANGE_MESSAGE_KEY, APIM_ASK_FOR_SECRETS_MESSAGE_KEY, getWidgetData, getEditorValues, getValues, askForSecrets, buildOnChange, onChangeWithOrigin, } from "./utils";
7
+ export { BLOB_ROOT, APIM_CONFIG_FILE_NAME, BLOB_DATA_FOLDER, BLOB_CONFIGS_FOLDER, buildBlobDataPath, buildBlobConfigPath, } from "./paths";
8
+ import deployNodeJS from "./node/deploy";
9
+ export { deployNodeJS };
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAElC;;GAEG;AAEH,OAAO,EACL,oBAAoB,EACpB,0BAA0B,EAC1B,gCAAgC,EAChC,aAAa,EACb,eAAe,EACf,SAAS,EACT,aAAa,EACb,aAAa,EACb,kBAAkB,GACnB,MAAM,SAAS,CAAC;AAUjB,OAAO,EACL,SAAS,EACT,qBAAqB,EACrB,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,SAAS,CAAC;AAEjB,OAAO,YAAY,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\n/**\n * @packageDocumentation https://aka.ms/apimdocs/portal/customwidgets\n */\n\nexport {\n APIM_EDITOR_DATA_KEY,\n APIM_ON_CHANGE_MESSAGE_KEY,\n APIM_ASK_FOR_SECRETS_MESSAGE_KEY,\n getWidgetData,\n getEditorValues,\n getValues,\n askForSecrets,\n buildOnChange,\n onChangeWithOrigin,\n} from \"./utils\";\nexport type {\n PortalData,\n EditorData,\n OnChange,\n Secrets,\n TargetModule,\n ValuesCommon,\n Environment,\n} from \"./utils\";\nexport {\n BLOB_ROOT,\n APIM_CONFIG_FILE_NAME,\n BLOB_DATA_FOLDER,\n BLOB_CONFIGS_FOLDER,\n buildBlobDataPath,\n buildBlobConfigPath,\n} from \"./paths\";\n\nimport deployNodeJS from \"./node/deploy\";\nexport { deployNodeJS };\nexport type { ServiceInformation } from \"./node/deploy\";\n"]}
@@ -0,0 +1,71 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT license.
3
+ import { __asyncValues } from "tslib";
4
+ import { BlobServiceClient } from "@azure/storage-blob";
5
+ import { buildBlobConfigPath, buildBlobDataPath } from "../paths";
6
+ import mime from "mime";
7
+ /**
8
+ * A service wrapping ContainerClient class to simplify blob handling
9
+ */
10
+ export class CustomWidgetBlobService {
11
+ /**
12
+ * @param blobStorageUrl - blob storage SAS URL
13
+ * @param name - name of the custom widget to be taken care of
14
+ */
15
+ constructor(blobStorageUrl, name) {
16
+ const container = "content";
17
+ const blobServiceClient = new BlobServiceClient(blobStorageUrl.replace(`/${container}`, ""));
18
+ this.containerClient = blobServiceClient.getContainerClient(container);
19
+ this.name = name;
20
+ this.pathWidget = buildBlobDataPath(name);
21
+ this.pathConfig = buildBlobConfigPath(name);
22
+ }
23
+ extractFileName(path) {
24
+ return path.split("/").pop();
25
+ }
26
+ async blobUpload(absolutePath, content) {
27
+ const fileName = this.extractFileName(absolutePath);
28
+ if (!fileName)
29
+ throw new Error("a fileName was not found in the absolutePath");
30
+ return this.containerClient.getBlockBlobClient(absolutePath).upload(content, content.length, {
31
+ blobHTTPHeaders: { blobContentType: mime.getType(fileName) || "application/octet-stream" },
32
+ });
33
+ }
34
+ async jsonUpload(absolutePath, json) {
35
+ return this.blobUpload(absolutePath, Buffer.from(JSON.stringify(json)));
36
+ }
37
+ async blobDownload(absolutePath) {
38
+ return this.containerClient.getBlockBlobClient(absolutePath).downloadToBuffer();
39
+ }
40
+ async dirDelete(absolutePath) {
41
+ var e_1, _a;
42
+ try {
43
+ for (var _b = __asyncValues(await this.containerClient.listBlobsFlat({ prefix: absolutePath })), _c; _c = await _b.next(), !_c.done;) {
44
+ const blob = _c.value;
45
+ await this.containerClient.deleteBlob(blob.name);
46
+ }
47
+ }
48
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
49
+ finally {
50
+ try {
51
+ if (_c && !_c.done && (_a = _b.return)) await _a.call(_b);
52
+ }
53
+ finally { if (e_1) throw e_1.error; }
54
+ }
55
+ }
56
+ async uploadWidgetDataFile(file, content) {
57
+ return this.blobUpload(this.pathWidget + file, content);
58
+ }
59
+ async cleanDataDir() {
60
+ return this.dirDelete(this.pathWidget);
61
+ }
62
+ async getConfig() {
63
+ const buffer = await this.blobDownload(this.pathConfig);
64
+ return JSON.parse(buffer.toString());
65
+ }
66
+ async uploadConfig(config) {
67
+ return this.jsonUpload(this.pathConfig, config);
68
+ }
69
+ }
70
+ export default CustomWidgetBlobService;
71
+ //# sourceMappingURL=CustomWidgetBlobService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CustomWidgetBlobService.js","sourceRoot":"","sources":["../../../src/node/CustomWidgetBlobService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { BlobServiceClient, BlockBlobUploadResponse } from \"@azure/storage-blob\";\nimport { buildBlobConfigPath, buildBlobDataPath } from \"../paths\";\nimport mime from \"mime\";\n\nexport type Config = Record<string, unknown>;\n\n/**\n * A service wrapping ContainerClient class to simplify blob handling\n */\nexport class CustomWidgetBlobService {\n readonly containerClient;\n readonly name;\n readonly pathWidget;\n readonly pathConfig;\n\n /**\n * @param blobStorageUrl - blob storage SAS URL\n * @param name - name of the custom widget to be taken care of\n */\n constructor(blobStorageUrl: string, name: string) {\n const container = \"content\";\n const blobServiceClient = new BlobServiceClient(blobStorageUrl.replace(`/${container}`, \"\"));\n this.containerClient = blobServiceClient.getContainerClient(container);\n this.name = name;\n this.pathWidget = buildBlobDataPath(name);\n this.pathConfig = buildBlobConfigPath(name);\n }\n\n private extractFileName(path: string): string | undefined {\n return path.split(\"/\").pop();\n }\n\n async blobUpload(absolutePath: string, content: Buffer): Promise<BlockBlobUploadResponse> {\n const fileName = this.extractFileName(absolutePath);\n if (!fileName) throw new Error(\"a fileName was not found in the absolutePath\");\n return this.containerClient.getBlockBlobClient(absolutePath).upload(content, content.length, {\n blobHTTPHeaders: { blobContentType: mime.getType(fileName) || \"application/octet-stream\" },\n });\n }\n\n async jsonUpload(\n absolutePath: string,\n json: Record<string, unknown>\n ): Promise<BlockBlobUploadResponse> {\n return this.blobUpload(absolutePath, Buffer.from(JSON.stringify(json)));\n }\n\n async blobDownload(absolutePath: string): Promise<Buffer> {\n return this.containerClient.getBlockBlobClient(absolutePath).downloadToBuffer();\n }\n\n async dirDelete(absolutePath: string): Promise<void> {\n for await (const blob of await this.containerClient.listBlobsFlat({ prefix: absolutePath })) {\n await this.containerClient.deleteBlob(blob.name);\n }\n }\n\n async uploadWidgetDataFile(file: string, content: Buffer): Promise<BlockBlobUploadResponse> {\n return this.blobUpload(this.pathWidget + file, content);\n }\n\n async cleanDataDir(): Promise<void> {\n return this.dirDelete(this.pathWidget);\n }\n\n async getConfig(): Promise<Config> {\n const buffer = await this.blobDownload(this.pathConfig);\n return JSON.parse(buffer.toString());\n }\n\n async uploadConfig(config: Config): Promise<BlockBlobUploadResponse> {\n return this.jsonUpload(this.pathConfig, config);\n }\n}\n\nexport default CustomWidgetBlobService;\n"]}
@@ -0,0 +1,7 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT license.
3
+ async function deploy(_serviceInformation, _name, _fallbackConfigPath) {
4
+ throw new Error("Only for Node.js");
5
+ }
6
+ export default deploy;
7
+ //# sourceMappingURL=deploy.browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy.browser.js","sourceRoot":"","sources":["../../../src/node/deploy.browser.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAElC,KAAK,UAAU,MAAM,CACnB,mBAA4B,EAC5B,KAAa,EACb,mBAA2B;IAE3B,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;AACtC,CAAC;AAED,eAAe,MAAM,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nasync function deploy(\n _serviceInformation: unknown,\n _name: string,\n _fallbackConfigPath: string\n): Promise<void> {\n throw new Error(\"Only for Node.js\");\n}\n\nexport default deploy;\n"]}
@@ -0,0 +1,56 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT license.
3
+ import CustomWidgetBlobService from "./CustomWidgetBlobService";
4
+ import { APIM_CONFIG_FILE_NAME } from "../paths";
5
+ import fs from "fs";
6
+ import getStorageSasUrl from "./getStorageSasUrl";
7
+ import readdir from "./readdir";
8
+ /**
9
+ * Deploys everything from /dist folder to the API Management DevPortals' blob storage.
10
+ *
11
+ * @param serviceInformation - service information for deployment
12
+ * @param name - name of the widget to be deployed
13
+ * @param fallbackConfigPath - local path to the config file (by default "./static/config.msapim.json")
14
+ * @param rootLocal - optional, root of the local folder with compiled project to be exported (by default "./dist")
15
+ */
16
+ async function deploy(serviceInformation, name, fallbackConfigPath = "./static/" + APIM_CONFIG_FILE_NAME, rootLocal = "./dist/") {
17
+ console.log("\n\n");
18
+ console.log("Starting deploy process of custom widget: " + name);
19
+ console.log("Please, sign in to your Azure account when prompted\n");
20
+ const blobStorageUrl = await getStorageSasUrl(serviceInformation);
21
+ const customWidgetBlobService = new CustomWidgetBlobService(blobStorageUrl, name);
22
+ let config;
23
+ try {
24
+ console.log("Looking for config file in the Azure blob storage");
25
+ config = await customWidgetBlobService.getConfig();
26
+ }
27
+ catch (e) {
28
+ console.log("Config not found.");
29
+ }
30
+ if (!config) {
31
+ console.log("Looking for a local config file in: " + fallbackConfigPath);
32
+ config = JSON.parse(fs.readFileSync(fallbackConfigPath).toString());
33
+ }
34
+ if (!config) {
35
+ throw new Error("Config file could not be loaded.");
36
+ }
37
+ console.log("Config file loaded\n");
38
+ const files = readdir("", rootLocal);
39
+ console.log("Starting upload of data files from the '" + rootLocal + "' folder\n");
40
+ await customWidgetBlobService.cleanDataDir();
41
+ const promises = [];
42
+ files.forEach((file) => {
43
+ const content = fs.readFileSync(rootLocal + file);
44
+ const promise = customWidgetBlobService
45
+ .uploadWidgetDataFile(file, content)
46
+ .then(() => console.log("Uploaded file: " + file));
47
+ promises.push(promise);
48
+ });
49
+ await Promise.all(promises);
50
+ console.log(files.length + " files has been uploaded\n");
51
+ config.deployedOn = new Date();
52
+ await customWidgetBlobService.uploadConfig(config);
53
+ console.log("Uploaded updated config");
54
+ }
55
+ export default deploy;
56
+ //# sourceMappingURL=deploy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../../src/node/deploy.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAElC,OAAO,uBAAmC,MAAM,2BAA2B,CAAC;AAC5E,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,gBAAgB,MAAM,oBAAoB,CAAC;AAClD,OAAO,OAAO,MAAM,WAAW,CAAC;AAehC;;;;;;;GAOG;AACH,KAAK,UAAU,MAAM,CACnB,kBAAsC,EACtC,IAAY,EACZ,kBAAkB,GAAG,WAAW,GAAG,qBAAqB,EACxD,YAAoB,SAAS;IAE7B,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACpB,OAAO,CAAC,GAAG,CAAC,4CAA4C,GAAG,IAAI,CAAC,CAAC;IACjE,OAAO,CAAC,GAAG,CAAC,uDAAuD,CAAC,CAAC;IAErE,MAAM,cAAc,GAAG,MAAM,gBAAgB,CAAC,kBAAkB,CAAC,CAAC;IAClE,MAAM,uBAAuB,GAAG,IAAI,uBAAuB,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;IAElF,IAAI,MAA0B,CAAC;IAC/B,IAAI;QACF,OAAO,CAAC,GAAG,CAAC,mDAAmD,CAAC,CAAC;QACjE,MAAM,GAAG,MAAM,uBAAuB,CAAC,SAAS,EAAE,CAAC;KACpD;IAAC,OAAO,CAAC,EAAE;QACV,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;KAClC;IACD,IAAI,CAAC,MAAM,EAAE;QACX,OAAO,CAAC,GAAG,CAAC,sCAAsC,GAAG,kBAAkB,CAAC,CAAC;QACzE,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,kBAAkB,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;KACrE;IACD,IAAI,CAAC,MAAM,EAAE;QACX,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;KACrD;IAED,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;IAEpC,MAAM,KAAK,GAAG,OAAO,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;IAErC,OAAO,CAAC,GAAG,CAAC,0CAA0C,GAAG,SAAS,GAAG,YAAY,CAAC,CAAC;IAEnF,MAAM,uBAAuB,CAAC,YAAY,EAAE,CAAC;IAE7C,MAAM,QAAQ,GAAoB,EAAE,CAAC;IACrC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QACrB,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;QAClD,MAAM,OAAO,GAAG,uBAAuB;aACpC,oBAAoB,CAAC,IAAI,EAAE,OAAO,CAAC;aACnC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC,CAAC;QACrD,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC;IACH,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAE5B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,4BAA4B,CAAC,CAAC;IAEzD,MAAM,CAAC,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC;IAC/B,MAAM,uBAAuB,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IACnD,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;AACzC,CAAC;AAED,eAAe,MAAM,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport CustomWidgetBlobService, { Config } from \"./CustomWidgetBlobService\";\nimport { APIM_CONFIG_FILE_NAME } from \"../paths\";\nimport fs from \"fs\";\nimport getStorageSasUrl from \"./getStorageSasUrl\";\nimport readdir from \"./readdir\";\n\n/**\n * resourceId - resource ID of API Management service \"subscriptions/[subscription-id]/resourceGroups/[resource-group-name]/providers/Microsoft.ApiManagement/service/[service-name]\"\n * managementApiEndpoint - URL with protocol (e.g. https://management.azure.com)\n * apiVersion - optional to override default (e.g. \"2019-01-01\")\n * tokenOverride - optional, provides token to use for auth, instead of 'az login' approach\n */\nexport type ServiceInformation = {\n resourceId: string;\n managementApiEndpoint: string;\n apiVersion?: string;\n tokenOverride?: string;\n};\n\n/**\n * Deploys everything from /dist folder to the API Management DevPortals' blob storage.\n *\n * @param serviceInformation - service information for deployment\n * @param name - name of the widget to be deployed\n * @param fallbackConfigPath - local path to the config file (by default \"./static/config.msapim.json\")\n * @param rootLocal - optional, root of the local folder with compiled project to be exported (by default \"./dist\")\n */\nasync function deploy(\n serviceInformation: ServiceInformation,\n name: string,\n fallbackConfigPath = \"./static/\" + APIM_CONFIG_FILE_NAME,\n rootLocal: string = \"./dist/\"\n): Promise<void> {\n console.log(\"\\n\\n\");\n console.log(\"Starting deploy process of custom widget: \" + name);\n console.log(\"Please, sign in to your Azure account when prompted\\n\");\n\n const blobStorageUrl = await getStorageSasUrl(serviceInformation);\n const customWidgetBlobService = new CustomWidgetBlobService(blobStorageUrl, name);\n\n let config: Config | undefined;\n try {\n console.log(\"Looking for config file in the Azure blob storage\");\n config = await customWidgetBlobService.getConfig();\n } catch (e) {\n console.log(\"Config not found.\");\n }\n if (!config) {\n console.log(\"Looking for a local config file in: \" + fallbackConfigPath);\n config = JSON.parse(fs.readFileSync(fallbackConfigPath).toString());\n }\n if (!config) {\n throw new Error(\"Config file could not be loaded.\");\n }\n\n console.log(\"Config file loaded\\n\");\n\n const files = readdir(\"\", rootLocal);\n\n console.log(\"Starting upload of data files from the '\" + rootLocal + \"' folder\\n\");\n\n await customWidgetBlobService.cleanDataDir();\n\n const promises: Promise<void>[] = [];\n files.forEach((file) => {\n const content = fs.readFileSync(rootLocal + file);\n const promise = customWidgetBlobService\n .uploadWidgetDataFile(file, content)\n .then(() => console.log(\"Uploaded file: \" + file));\n promises.push(promise);\n });\n await Promise.all(promises);\n\n console.log(files.length + \" files has been uploaded\\n\");\n\n config.deployedOn = new Date();\n await customWidgetBlobService.uploadConfig(config);\n console.log(\"Uploaded updated config\");\n}\n\nexport default deploy;\n"]}
@@ -0,0 +1,33 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT license.
3
+ import { InteractiveBrowserCredential } from "@azure/identity";
4
+ import { getClient } from "@azure-rest/core-client";
5
+ async function getAccessToken(managementApiEndpoint) {
6
+ const credentials = new InteractiveBrowserCredential();
7
+ const scope = `${managementApiEndpoint}/user_impersonation`;
8
+ const { token } = await credentials.getToken(scope);
9
+ return `Bearer ${token}`;
10
+ }
11
+ /**
12
+ * Function to get storage SAS URL.
13
+ *
14
+ * @returns storage SAS URL
15
+ */
16
+ async function getStorageSasUrl({ managementApiEndpoint, resourceId, apiVersion = "2019-01-01", tokenOverride, }) {
17
+ var _a;
18
+ const httpClient = getClient(`${managementApiEndpoint}/${resourceId}`, { apiVersion });
19
+ const response = await httpClient
20
+ .pathUnchecked(`/portalSettings/mediaContent/listSecrets?apiVersion=${apiVersion}`) // TODO
21
+ .post({
22
+ headers: {
23
+ "If-Match": "*",
24
+ "Content-Type": "application/json",
25
+ Authorization: tokenOverride !== null && tokenOverride !== void 0 ? tokenOverride : (await getAccessToken(managementApiEndpoint)),
26
+ },
27
+ });
28
+ if (!((_a = response === null || response === void 0 ? void 0 : response.body) === null || _a === void 0 ? void 0 : _a.containerSasUrl))
29
+ throw new Error("Could not get storage SAS URL");
30
+ return response.body.containerSasUrl;
31
+ }
32
+ export default getStorageSasUrl;
33
+ //# sourceMappingURL=getStorageSasUrl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getStorageSasUrl.js","sourceRoot":"","sources":["../../../src/node/getStorageSasUrl.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAElC,OAAO,EAAE,4BAA4B,EAAE,MAAM,iBAAiB,CAAC;AAE/D,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEpD,KAAK,UAAU,cAAc,CAAC,qBAA6B;IACzD,MAAM,WAAW,GAAG,IAAI,4BAA4B,EAAE,CAAC;IACvD,MAAM,KAAK,GAAG,GAAG,qBAAqB,qBAAqB,CAAC;IAC5D,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACpD,OAAO,UAAU,KAAK,EAAE,CAAC;AAC3B,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,gBAAgB,CAAC,EAC9B,qBAAqB,EACrB,UAAU,EACV,UAAU,GAAG,YAAY,EACzB,aAAa,GACM;;IACnB,MAAM,UAAU,GAAG,SAAS,CAAC,GAAG,qBAAqB,IAAI,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;IACvF,MAAM,QAAQ,GAAG,MAAM,UAAU;SAC9B,aAAa,CAAC,uDAAuD,UAAU,EAAE,CAAC,CAAC,OAAO;SAC1F,IAAI,CAAC;QACJ,OAAO,EAAE;YACP,UAAU,EAAE,GAAG;YACf,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,aAAa,aAAb,aAAa,cAAb,aAAa,GAAI,CAAC,MAAM,cAAc,CAAC,qBAAqB,CAAC,CAAC;SAC9E;KACF,CAAC,CAAC;IAEL,IAAI,CAAC,CAAA,MAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,IAAI,0CAAE,eAAe,CAAA;QAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACvF,OAAO,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC;AACvC,CAAC;AAED,eAAe,gBAAgB,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { InteractiveBrowserCredential } from \"@azure/identity\";\nimport { ServiceInformation } from \"./deploy\";\nimport { getClient } from \"@azure-rest/core-client\";\n\nasync function getAccessToken(managementApiEndpoint: string): Promise<string> {\n const credentials = new InteractiveBrowserCredential();\n const scope = `${managementApiEndpoint}/user_impersonation`;\n const { token } = await credentials.getToken(scope);\n return `Bearer ${token}`;\n}\n\n/**\n * Function to get storage SAS URL.\n *\n * @returns storage SAS URL\n */\nasync function getStorageSasUrl({\n managementApiEndpoint,\n resourceId,\n apiVersion = \"2019-01-01\",\n tokenOverride,\n}: ServiceInformation): Promise<string> {\n const httpClient = getClient(`${managementApiEndpoint}/${resourceId}`, { apiVersion });\n const response = await httpClient\n .pathUnchecked(`/portalSettings/mediaContent/listSecrets?apiVersion=${apiVersion}`) // TODO\n .post({\n headers: {\n \"If-Match\": \"*\",\n \"Content-Type\": \"application/json\",\n Authorization: tokenOverride ?? (await getAccessToken(managementApiEndpoint)),\n },\n });\n\n if (!response?.body?.containerSasUrl) throw new Error(\"Could not get storage SAS URL\");\n return response.body.containerSasUrl;\n}\n\nexport default getStorageSasUrl;\n"]}
@@ -0,0 +1,19 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT license.
3
+ import fs from "fs";
4
+ import path from "path";
5
+ function readdir(dir, root) {
6
+ const results = [];
7
+ fs.readdirSync(root + dir).forEach((file) => {
8
+ const stat = fs.statSync(root + dir + path.sep + file);
9
+ if (stat && stat.isDirectory()) {
10
+ results.push(...readdir(dir + file + path.sep, root));
11
+ }
12
+ else {
13
+ results.push(dir + file);
14
+ }
15
+ });
16
+ return results;
17
+ }
18
+ export default readdir;
19
+ //# sourceMappingURL=readdir.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readdir.js","sourceRoot":"","sources":["../../../src/node/readdir.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAElC,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,SAAS,OAAO,CAAC,GAAW,EAAE,IAAY;IACxC,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,EAAE,CAAC,WAAW,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QAC1C,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,GAAG,GAAG,GAAG,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;QACvD,IAAI,IAAI,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE;YAC9B,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;SACvD;aAAM;YACL,OAAO,CAAC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;SAC1B;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,eAAe,OAAO,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport fs from \"fs\";\nimport path from \"path\";\n\nfunction readdir(dir: string, root: string): string[] {\n const results: string[] = [];\n\n fs.readdirSync(root + dir).forEach((file) => {\n const stat = fs.statSync(root + dir + path.sep + file);\n if (stat && stat.isDirectory()) {\n results.push(...readdir(dir + file + path.sep, root));\n } else {\n results.push(dir + file);\n }\n });\n\n return results;\n}\n\nexport default readdir;\n"]}
@@ -0,0 +1,27 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT license.
3
+ /** root of the blob storage folder */
4
+ export const BLOB_ROOT = "custom-widgets";
5
+ /** name of the blob storage folder with widget implementation */
6
+ export const BLOB_DATA_FOLDER = "data";
7
+ /** name of the blob storage folder with widget configs */
8
+ export const BLOB_CONFIGS_FOLDER = "configs";
9
+ /** name of the configuration file */
10
+ export const APIM_CONFIG_FILE_NAME = "config.msapim.json";
11
+ /**
12
+ * Generate relative path for widgets' data on the blob storage
13
+ *
14
+ * @param name - name of the widget
15
+ */
16
+ export function buildBlobDataPath(name) {
17
+ return `${BLOB_ROOT}/${BLOB_DATA_FOLDER}/${name}/`;
18
+ }
19
+ /**
20
+ * Generate relative path for widgets' config on the blob storage
21
+ *
22
+ * @param name - name of the widget
23
+ */
24
+ export function buildBlobConfigPath(name) {
25
+ return `${BLOB_ROOT}/${BLOB_CONFIGS_FOLDER}/${name}/${APIM_CONFIG_FILE_NAME}`;
26
+ }
27
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/paths.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAElC,sCAAsC;AACtC,MAAM,CAAC,MAAM,SAAS,GAAG,gBAAgB,CAAC;AAC1C,iEAAiE;AACjE,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC;AACvC,0DAA0D;AAC1D,MAAM,CAAC,MAAM,mBAAmB,GAAG,SAAS,CAAC;AAC7C,qCAAqC;AACrC,MAAM,CAAC,MAAM,qBAAqB,GAAG,oBAAoB,CAAC;AAE1D;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,GAAG,SAAS,IAAI,gBAAgB,IAAI,IAAI,GAAG,CAAC;AACrD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,OAAO,GAAG,SAAS,IAAI,mBAAmB,IAAI,IAAI,IAAI,qBAAqB,EAAE,CAAC;AAChF,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\n/** root of the blob storage folder */\nexport const BLOB_ROOT = \"custom-widgets\";\n/** name of the blob storage folder with widget implementation */\nexport const BLOB_DATA_FOLDER = \"data\";\n/** name of the blob storage folder with widget configs */\nexport const BLOB_CONFIGS_FOLDER = \"configs\";\n/** name of the configuration file */\nexport const APIM_CONFIG_FILE_NAME = \"config.msapim.json\";\n\n/**\n * Generate relative path for widgets' data on the blob storage\n *\n * @param name - name of the widget\n */\nexport function buildBlobDataPath(name: string): string {\n return `${BLOB_ROOT}/${BLOB_DATA_FOLDER}/${name}/`;\n}\n\n/**\n * Generate relative path for widgets' config on the blob storage\n *\n * @param name - name of the widget\n */\nexport function buildBlobConfigPath(name: string): string {\n return `${BLOB_ROOT}/${BLOB_CONFIGS_FOLDER}/${name}/${APIM_CONFIG_FILE_NAME}`;\n}\n"]}
@@ -0,0 +1,123 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT license.
3
+ /**
4
+ * Key for a search param, from which editor data will be loaded from.
5
+ */
6
+ export const APIM_EDITOR_DATA_KEY = "editorData";
7
+ /**
8
+ * Key for a post message object, it's used to propagate changes from editor to the DevPortal. Used to prevent interference with other applications.
9
+ */
10
+ export const APIM_ON_CHANGE_MESSAGE_KEY = "customInputValueChangedMSAPIM";
11
+ /**
12
+ * Key for a post message object, it's used to request and send secrets - token and user id, from the DevPortal. Used to prevent interference with other applications.
13
+ */
14
+ export const APIM_ASK_FOR_SECRETS_MESSAGE_KEY = "askForSecretsMSAPIM";
15
+ function parseWidgetData(urlSearchParams) {
16
+ var _a;
17
+ try {
18
+ const urlEditorParams = JSON.parse(decodeURIComponent((_a = urlSearchParams.get(APIM_EDITOR_DATA_KEY)) !== null && _a !== void 0 ? _a : ""));
19
+ // if (!("origin" in urlEditorParams)) {
20
+ // console.error(
21
+ // "Could not get 'origin' from the search params of the URL:\n" + self.location.href
22
+ // );
23
+ // }
24
+ return urlEditorParams;
25
+ }
26
+ catch (e) {
27
+ console.error(`Could not get '${APIM_EDITOR_DATA_KEY}' from the search params of the URL:\n` +
28
+ self.location, e);
29
+ return { values: {}, environment: "error", instanceId: "error" };
30
+ }
31
+ }
32
+ export function getWidgetDataPure(urlSearchParams) {
33
+ return parseWidgetData(urlSearchParams);
34
+ }
35
+ /**
36
+ * Function to get all data related to the widget including technical values not expected to be needed in most cases.
37
+ * Intended mostly for internal use, API might change. Consider using getValues or getEditorValues instead.
38
+ */
39
+ export function getWidgetData() {
40
+ return getWidgetDataPure(new URLSearchParams(self.location.search));
41
+ }
42
+ export function getEditorValuesPure(urlSearchParams) {
43
+ return getWidgetDataPure(urlSearchParams).values;
44
+ }
45
+ /**
46
+ * Function to get values you've set in the admin editor window.
47
+ */
48
+ export function getEditorValues() {
49
+ return getEditorValuesPure(new URLSearchParams(self.location.search));
50
+ }
51
+ export function getValuesPure(valuesDefault, urlSearchParams) {
52
+ const values = Object.assign({}, valuesDefault); // set Obj to contain all possible values and prefill default value
53
+ const urlValues = parseWidgetData(urlSearchParams).values;
54
+ Object.keys(values).forEach((key) => {
55
+ const value = urlValues[key];
56
+ if (value != null && value !== "")
57
+ values[key] = value; // if value is specified in the URL, replace the default value
58
+ });
59
+ return values;
60
+ }
61
+ /**
62
+ * Function to get values you've set in the admin editor window. Undefined/empty values are replaced with default values.
63
+ *
64
+ * @param valuesDefault - object with your default values to use, just import valuesDefault object from values.ts folder
65
+ */
66
+ export function getValues(valuesDefault) {
67
+ return getValuesPure(valuesDefault, new URLSearchParams(self.location.search));
68
+ }
69
+ /**
70
+ * The onChange function itself with 'origin' provided as a param.
71
+ *
72
+ * @param origin - web content's origin (URL) of your Dev Portal to send changes to
73
+ * @param instanceId - ID of this particular instance of the widget
74
+ * @param values - values that changed
75
+ */
76
+ export function onChangeWithOrigin(origin, instanceId, values) {
77
+ Object.entries(values).forEach(([key, value]) => {
78
+ self.parent.postMessage({ [APIM_ON_CHANGE_MESSAGE_KEY]: { key, value, instanceId } }, origin);
79
+ });
80
+ }
81
+ /**
82
+ * Build onChange function, which you can use, to send changed data from the editor.
83
+ */
84
+ export function buildOnChange() {
85
+ const { instanceId } = getWidgetData();
86
+ return (values) => onChangeWithOrigin("*", instanceId, values);
87
+ }
88
+ /**
89
+ * Request secrets - token & userId, from the Dev portal parent window.
90
+ *
91
+ * @param targetModule - is the function invoke from the main "app" window or the admin "editor"?
92
+ */
93
+ export async function askForSecrets(targetModule) {
94
+ let receiveSecrets;
95
+ const promise = new Promise((resolve, reject) => {
96
+ const { instanceId, environment } = getWidgetData();
97
+ receiveSecrets = ({ data }) => {
98
+ if (!(APIM_ASK_FOR_SECRETS_MESSAGE_KEY in data))
99
+ return;
100
+ const secrets = data[APIM_ASK_FOR_SECRETS_MESSAGE_KEY];
101
+ if (typeof secrets !== "object" || !("managementApiUrl" in secrets)) {
102
+ reject("Secrets send by Dev Portal are invalid");
103
+ }
104
+ resolve(secrets);
105
+ };
106
+ self.addEventListener("message", receiveSecrets);
107
+ const message = {
108
+ [APIM_ASK_FOR_SECRETS_MESSAGE_KEY]: {
109
+ instanceId,
110
+ origin: self.location.origin,
111
+ targetModule,
112
+ },
113
+ };
114
+ if (targetModule === "app" && environment === "development") {
115
+ self.parent.parent.postMessage(message, "*");
116
+ }
117
+ else {
118
+ self.parent.postMessage(message, "*");
119
+ }
120
+ });
121
+ return promise.finally(() => self.removeEventListener("message", receiveSecrets));
122
+ }
123
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAElC;;GAEG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,YAAY,CAAC;AACjD;;GAEG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,+BAA+B,CAAC;AAC1E;;GAEG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,qBAAqB,CAAC;AA2BtE,SAAS,eAAe,CACtB,eAAgC;;IAEhC,IAAI;QACF,MAAM,eAAe,GAAuB,IAAI,CAAC,KAAK,CACpD,kBAAkB,CAAC,MAAA,eAAe,CAAC,GAAG,CAAC,oBAAoB,CAAC,mCAAI,EAAE,CAAC,CACpE,CAAC;QAEF,wCAAwC;QACxC,mBAAmB;QACnB,yFAAyF;QACzF,OAAO;QACP,IAAI;QACJ,OAAO,eAAe,CAAC;KACxB;IAAC,OAAO,CAAC,EAAE;QACV,OAAO,CAAC,KAAK,CACX,kBAAkB,oBAAoB,wCAAwC;YAC5E,IAAI,CAAC,QAAQ,EACf,CAAC,CACF,CAAC;QACF,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;KAClE;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,eAAgC;IAEhC,OAAO,eAAe,CAAS,eAAe,CAAC,CAAC;AAClD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa;IAC3B,OAAO,iBAAiB,CAAC,IAAI,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,eAAgC;IAEhC,OAAO,iBAAiB,CAAS,eAAe,CAAC,CAAC,MAAM,CAAC;AAC3D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,mBAAmB,CAAS,IAAI,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,aAAqB,EACrB,eAAgC;IAEhC,MAAM,MAAM,qBAAQ,aAAa,CAAE,CAAC,CAAC,mEAAmE;IACxG,MAAM,SAAS,GAAG,eAAe,CAAS,eAAe,CAAC,CAAC,MAAM,CAAC;IAElE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,GAAiB,EAAE,EAAE;QAChD,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,EAAE;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,KAA2B,CAAC,CAAC,8DAA8D;IAC9I,CAAC,CAAC,CAAC;IACH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAA8B,aAAqB;IAC1E,OAAO,aAAa,CAAC,aAAa,EAAE,IAAI,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;AACjF,CAAC;AAOD;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAAc,EACd,UAAkB,EAClB,MAAc;IAEd,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;QAC9C,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,0BAA0B,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;IAChG,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,EAAE,UAAU,EAAE,GAAG,aAAa,EAAE,CAAC;IACvC,OAAO,CAAC,MAAuB,EAAE,EAAE,CAAC,kBAAkB,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;AAClF,CAAC;AAkBD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,YAA0B;IAC5D,IAAI,cAAyC,CAAC;IAE9C,MAAM,OAAO,GAAG,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACvD,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,GAAe,aAAa,EAAE,CAAC;QAEhE,cAAc,GAAG,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE;YAC5B,IAAI,CAAC,CAAC,gCAAgC,IAAI,IAAI,CAAC;gBAAE,OAAO;YAExD,MAAM,OAAO,GAAG,IAAI,CAAC,gCAAgC,CAAC,CAAC;YACvD,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,kBAAkB,IAAI,OAAO,CAAC,EAAE;gBACnE,MAAM,CAAC,wCAAwC,CAAC,CAAC;aAClD;YAED,OAAO,CAAC,OAAO,CAAC,CAAC;QACnB,CAAC,CAAC;QAEF,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;QAEjD,MAAM,OAAO,GAAG;YACd,CAAC,gCAAgC,CAAC,EAAE;gBAClC,UAAU;gBACV,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM;gBAC5B,YAAY;aACb;SACF,CAAC;QAEF,IAAI,YAAY,KAAK,KAAK,IAAI,WAAW,KAAK,aAAa,EAAE;YAC3D,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;SAC9C;aAAM;YACL,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;SACvC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,mBAAmB,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC;AACpF,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\n/**\n * Key for a search param, from which editor data will be loaded from.\n */\nexport const APIM_EDITOR_DATA_KEY = \"editorData\";\n/**\n * Key for a post message object, it's used to propagate changes from editor to the DevPortal. Used to prevent interference with other applications.\n */\nexport const APIM_ON_CHANGE_MESSAGE_KEY = \"customInputValueChangedMSAPIM\";\n/**\n * Key for a post message object, it's used to request and send secrets - token and user id, from the DevPortal. Used to prevent interference with other applications.\n */\nexport const APIM_ASK_FOR_SECRETS_MESSAGE_KEY = \"askForSecretsMSAPIM\";\n\n/**\n * Base of a values obj\n */\nexport type ValuesCommon = Record<string, unknown>;\n/**\n * All possible runtime environments\n */\nexport type Environment = \"development\" | \"publishing\" | \"runtime\" | \"error\";\n\n/** Information about the widget instance received from the Dev Portal */\nexport interface PortalData {\n // /** web content's origin (URL) of your Dev Portal */\n // origin: string;\n /** current runtime environment */\n environment: Environment;\n /** ID of this particular instance of the widget */\n instanceId: string;\n}\n\n/** JSON object with all the data you'll receive from the Dev Portal */\nexport interface EditorData<Values extends ValuesCommon> extends PortalData {\n /** values you've set in the admin editor window */\n values: Partial<Values>;\n}\n\nfunction parseWidgetData<Values extends ValuesCommon>(\n urlSearchParams: URLSearchParams\n): EditorData<Values> {\n try {\n const urlEditorParams: EditorData<Values> = JSON.parse(\n decodeURIComponent(urlSearchParams.get(APIM_EDITOR_DATA_KEY) ?? \"\")\n );\n\n // if (!(\"origin\" in urlEditorParams)) {\n // console.error(\n // \"Could not get 'origin' from the search params of the URL:\\n\" + self.location.href\n // );\n // }\n return urlEditorParams;\n } catch (e) {\n console.error(\n `Could not get '${APIM_EDITOR_DATA_KEY}' from the search params of the URL:\\n` +\n self.location,\n e\n );\n return { values: {}, environment: \"error\", instanceId: \"error\" };\n }\n}\n\nexport function getWidgetDataPure<Values extends ValuesCommon>(\n urlSearchParams: URLSearchParams\n): EditorData<Values> {\n return parseWidgetData<Values>(urlSearchParams);\n}\n\n/**\n * Function to get all data related to the widget including technical values not expected to be needed in most cases.\n * Intended mostly for internal use, API might change. Consider using getValues or getEditorValues instead.\n */\nexport function getWidgetData<Values extends ValuesCommon>(): EditorData<Values> {\n return getWidgetDataPure(new URLSearchParams(self.location.search));\n}\n\nexport function getEditorValuesPure<Values extends ValuesCommon>(\n urlSearchParams: URLSearchParams\n): Partial<Values> {\n return getWidgetDataPure<Values>(urlSearchParams).values;\n}\n\n/**\n * Function to get values you've set in the admin editor window.\n */\nexport function getEditorValues<Values extends ValuesCommon>(): Partial<Values> {\n return getEditorValuesPure<Values>(new URLSearchParams(self.location.search));\n}\n\nexport function getValuesPure<Values extends ValuesCommon>(\n valuesDefault: Values,\n urlSearchParams: URLSearchParams\n): Values {\n const values = { ...valuesDefault }; // set Obj to contain all possible values and prefill default value\n const urlValues = parseWidgetData<Values>(urlSearchParams).values;\n\n Object.keys(values).forEach((key: keyof Values) => {\n const value = urlValues[key];\n if (value != null && value !== \"\") values[key] = value as Values[typeof key]; // if value is specified in the URL, replace the default value\n });\n return values;\n}\n\n/**\n * Function to get values you've set in the admin editor window. Undefined/empty values are replaced with default values.\n *\n * @param valuesDefault - object with your default values to use, just import valuesDefault object from values.ts folder\n */\nexport function getValues<Values extends ValuesCommon>(valuesDefault: Values): Values {\n return getValuesPure(valuesDefault, new URLSearchParams(self.location.search));\n}\n\n/**\n * Type of the onChange function.\n */\nexport type OnChange<Values extends ValuesCommon> = (values: Partial<Values>) => void;\n\n/**\n * The onChange function itself with 'origin' provided as a param.\n *\n * @param origin - web content's origin (URL) of your Dev Portal to send changes to\n * @param instanceId - ID of this particular instance of the widget\n * @param values - values that changed\n */\nexport function onChangeWithOrigin<Values extends ValuesCommon>(\n origin: string,\n instanceId: string,\n values: Values\n): void {\n Object.entries(values).forEach(([key, value]) => {\n self.parent.postMessage({ [APIM_ON_CHANGE_MESSAGE_KEY]: { key, value, instanceId } }, origin);\n });\n}\n\n/**\n * Build onChange function, which you can use, to send changed data from the editor.\n */\nexport function buildOnChange<Values extends ValuesCommon>(): OnChange<Values> {\n const { instanceId } = getWidgetData();\n return (values: Partial<Values>) => onChangeWithOrigin(\"*\", instanceId, values);\n}\n\n/**\n * Possible target modules\n * \"app\" for main application which is embedded in your Dev Portal\n * \"editor\" for form in admin panel\n */\nexport type TargetModule = \"app\" | \"editor\";\n/**\n * Secrets needed for communication with Dev Portal back-end\n */\nexport type Secrets = {\n managementApiUrl: string;\n apiVersion: string;\n userId?: string;\n token?: string;\n};\n\n/**\n * Request secrets - token & userId, from the Dev portal parent window.\n *\n * @param targetModule - is the function invoke from the main \"app\" window or the admin \"editor\"?\n */\nexport async function askForSecrets(targetModule: TargetModule): Promise<Secrets> {\n let receiveSecrets: (e: MessageEvent) => void;\n\n const promise = new Promise<Secrets>((resolve, reject) => {\n const { instanceId, environment }: PortalData = getWidgetData();\n\n receiveSecrets = ({ data }) => {\n if (!(APIM_ASK_FOR_SECRETS_MESSAGE_KEY in data)) return;\n\n const secrets = data[APIM_ASK_FOR_SECRETS_MESSAGE_KEY];\n if (typeof secrets !== \"object\" || !(\"managementApiUrl\" in secrets)) {\n reject(\"Secrets send by Dev Portal are invalid\");\n }\n\n resolve(secrets);\n };\n\n self.addEventListener(\"message\", receiveSecrets);\n\n const message = {\n [APIM_ASK_FOR_SECRETS_MESSAGE_KEY]: {\n instanceId,\n origin: self.location.origin,\n targetModule,\n },\n };\n\n if (targetModule === \"app\" && environment === \"development\") {\n self.parent.parent.postMessage(message, \"*\");\n } else {\n self.parent.postMessage(message, \"*\");\n }\n });\n\n return promise.finally(() => self.removeEventListener(\"message\", receiveSecrets));\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,113 @@
1
+ {
2
+ "name": "@azure/api-management-custom-widgets-tools",
3
+ "version": "1.0.0-beta.1",
4
+ "author": "Microsoft Corporation",
5
+ "license": "MIT",
6
+ "sdk-type": "client",
7
+ "homepage": "https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/apimanagement/api-management-custom-widgets-tools/README.md",
8
+ "repository": "github:Azure/azure-sdk-for-js",
9
+ "browser": {
10
+ "./dist-esm/src/node/deploy.js": "./dist-esm/src/node/deploy.browser.js"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/Azure/azure-sdk-for-js/issues"
14
+ },
15
+ "engines": {
16
+ "node": ">=12.0.0"
17
+ },
18
+ "keywords": [
19
+ "azure",
20
+ "cloud",
21
+ "typescript",
22
+ "apimanagement",
23
+ "developer-portal"
24
+ ],
25
+ "sideEffects": false,
26
+ "files": [
27
+ "dist/",
28
+ "dist-esm/src/",
29
+ "types/latest/api-management-custom-widgets-tools.d.ts",
30
+ "LICENSE"
31
+ ],
32
+ "main": "dist/index.js",
33
+ "module": "dist-esm/src/index.js",
34
+ "types": "types/latest/api-management-custom-widgets-tools.d.ts",
35
+ "scripts": {
36
+ "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit",
37
+ "build:browser": "tsc -p . && dev-tool run bundle --browser-test=false",
38
+ "build:node": "tsc -p . && dev-tool run bundle",
39
+ "build:test": "npm run clean && tsc -p . && dev-tool run bundle",
40
+ "build:samples": "echo skip",
41
+ "build": "npm run clean && tsc -p . && dev-tool run bundle --browser-test=false && api-extractor run --local",
42
+ "check-format": "prettier --list-different --config ../../../.prettierrc.json --ignore-path ../../../.prettierignore --ignore-path ./.prettierignore \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"",
43
+ "clean": "rimraf dist dist-* types *.tgz *.log",
44
+ "extract-api": "tsc -p . && api-extractor run --local",
45
+ "format": "prettier --write --config ../../../.prettierrc.json --ignore-path ../../../.prettierignore --ignore-path ./.prettierignore \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"",
46
+ "integration-test:browser": "dev-tool run test:browser",
47
+ "integration-test:node": "echo skip",
48
+ "integration-test": "npm run integration-test:node && npm run integration-test:browser",
49
+ "lint:fix": "eslint package.json api-extractor.json src test --ext .ts --fix --fix-type [problem,suggestion]",
50
+ "lint": "eslint package.json api-extractor.json src test --ext .ts",
51
+ "pack": "npm pack 2>&1",
52
+ "test:browser": "echo skip",
53
+ "test:node": "npm run build:test && npm run unit-test:node && npm run integration-test:node",
54
+ "test": "npm run clean && npm run build:test && npm run unit-test && npm run integration-test",
55
+ "unit-test:browser": "echo skip",
56
+ "unit-test:node": "npm run integration-test:node",
57
+ "unit-test": "npm run unit-test:node && npm run unit-test:browser"
58
+ },
59
+ "devDependencies": {
60
+ "@azure/dev-tool": "^1.0.0",
61
+ "@azure-tools/test-recorder": "^2.0.0",
62
+ "@microsoft/api-extractor": "7.18.11",
63
+ "@types/chai": "^4.1.6",
64
+ "@types/mocha": "^7.0.2",
65
+ "@types/mime": "~2.0.3",
66
+ "@types/node": "^12.0.0",
67
+ "@types/glob": "^7.1.1",
68
+ "@types/inquirer": "^8.2.1",
69
+ "@types/yargs": "^17.0.10",
70
+ "@types/yargs-parser": "^21.0.0",
71
+ "@azure/eslint-plugin-azure-sdk": "^3.0.0",
72
+ "chai": "^4.2.0",
73
+ "cross-env": "^7.0.2",
74
+ "eslint": "^7.15.0",
75
+ "mocha": "^7.1.1",
76
+ "mocha-junit-reporter": "^2.0.0",
77
+ "prettier": "^2.5.1",
78
+ "nyc": "^15.0.0",
79
+ "rimraf": "^3.0.0",
80
+ "typescript": "~4.6.0",
81
+ "util": "^0.12.1",
82
+ "dotenv": "^8.2.0",
83
+ "@azure/test-utils": "^1.0.0",
84
+ "@types/mustache": "4.1.2",
85
+ "sinon": "^9.0.4",
86
+ "karma": "^6.2.0",
87
+ "karma-chrome-launcher": "^3.0.0",
88
+ "karma-coverage": "^2.0.0",
89
+ "karma-edge-launcher": "^0.4.2",
90
+ "karma-env-preprocessor": "^0.1.1",
91
+ "karma-firefox-launcher": "^1.1.0",
92
+ "karma-ie-launcher": "^1.0.0",
93
+ "karma-json-preprocessor": "^0.3.3",
94
+ "karma-json-to-file-reporter": "^1.0.1",
95
+ "karma-junit-reporter": "^2.0.1",
96
+ "karma-mocha": "^2.0.1",
97
+ "karma-mocha-reporter": "^2.2.5",
98
+ "@types/sinon": "^9.0.4"
99
+ },
100
+ "dependencies": {
101
+ "@azure/identity": "^2.1.0",
102
+ "@azure-rest/core-client": "^1.0.0-beta.10",
103
+ "@azure/storage-blob": "^12.9.0",
104
+ "mime": "^3.0.0",
105
+ "mustache": "^4.2.0",
106
+ "prettier": "^2.5.1",
107
+ "glob": "^7.1.2",
108
+ "tslib": "^2.2.0",
109
+ "typescript": "~4.6.4",
110
+ "rollup": "~2.75.5",
111
+ "@rollup/plugin-node-resolve": "~13.3.0"
112
+ }
113
+ }
@@ -0,0 +1,154 @@
1
+ /**
2
+ * @packageDocumentation https://aka.ms/apimdocs/portal/customwidgets
3
+ */
4
+
5
+ /**
6
+ * Key for a post message object, it's used to request and send secrets - token and user id, from the DevPortal. Used to prevent interference with other applications.
7
+ */
8
+ export declare const APIM_ASK_FOR_SECRETS_MESSAGE_KEY = "askForSecretsMSAPIM";
9
+
10
+ /** name of the configuration file */
11
+ export declare const APIM_CONFIG_FILE_NAME = "config.msapim.json";
12
+
13
+ /**
14
+ * Key for a search param, from which editor data will be loaded from.
15
+ */
16
+ export declare const APIM_EDITOR_DATA_KEY = "editorData";
17
+
18
+ /**
19
+ * Key for a post message object, it's used to propagate changes from editor to the DevPortal. Used to prevent interference with other applications.
20
+ */
21
+ export declare const APIM_ON_CHANGE_MESSAGE_KEY = "customInputValueChangedMSAPIM";
22
+
23
+ /**
24
+ * Request secrets - token & userId, from the Dev portal parent window.
25
+ *
26
+ * @param targetModule - is the function invoke from the main "app" window or the admin "editor"?
27
+ */
28
+ export declare function askForSecrets(targetModule: TargetModule): Promise<Secrets>;
29
+
30
+ /** name of the blob storage folder with widget configs */
31
+ export declare const BLOB_CONFIGS_FOLDER = "configs";
32
+
33
+ /** name of the blob storage folder with widget implementation */
34
+ export declare const BLOB_DATA_FOLDER = "data";
35
+
36
+ /** root of the blob storage folder */
37
+ export declare const BLOB_ROOT = "custom-widgets";
38
+
39
+ /**
40
+ * Generate relative path for widgets' config on the blob storage
41
+ *
42
+ * @param name - name of the widget
43
+ */
44
+ export declare function buildBlobConfigPath(name: string): string;
45
+
46
+ /**
47
+ * Generate relative path for widgets' data on the blob storage
48
+ *
49
+ * @param name - name of the widget
50
+ */
51
+ export declare function buildBlobDataPath(name: string): string;
52
+
53
+ /**
54
+ * Build onChange function, which you can use, to send changed data from the editor.
55
+ */
56
+ export declare function buildOnChange<Values extends ValuesCommon>(): OnChange<Values>;
57
+
58
+ /**
59
+ * Deploys everything from /dist folder to the API Management DevPortals' blob storage.
60
+ *
61
+ * @param serviceInformation - service information for deployment
62
+ * @param name - name of the widget to be deployed
63
+ * @param fallbackConfigPath - local path to the config file (by default "./static/config.msapim.json")
64
+ * @param rootLocal - optional, root of the local folder with compiled project to be exported (by default "./dist")
65
+ */
66
+ export declare function deployNodeJS(serviceInformation: ServiceInformation, name: string, fallbackConfigPath?: string, rootLocal?: string): Promise<void>;
67
+
68
+ /** JSON object with all the data you'll receive from the Dev Portal */
69
+ export declare interface EditorData<Values extends ValuesCommon> extends PortalData {
70
+ /** values you've set in the admin editor window */
71
+ values: Partial<Values>;
72
+ }
73
+
74
+ /**
75
+ * All possible runtime environments
76
+ */
77
+ export declare type Environment = "development" | "publishing" | "runtime" | "error";
78
+
79
+ /**
80
+ * Function to get values you've set in the admin editor window.
81
+ */
82
+ export declare function getEditorValues<Values extends ValuesCommon>(): Partial<Values>;
83
+
84
+ /**
85
+ * Function to get values you've set in the admin editor window. Undefined/empty values are replaced with default values.
86
+ *
87
+ * @param valuesDefault - object with your default values to use, just import valuesDefault object from values.ts folder
88
+ */
89
+ export declare function getValues<Values extends ValuesCommon>(valuesDefault: Values): Values;
90
+
91
+ /**
92
+ * Function to get all data related to the widget including technical values not expected to be needed in most cases.
93
+ * Intended mostly for internal use, API might change. Consider using getValues or getEditorValues instead.
94
+ */
95
+ export declare function getWidgetData<Values extends ValuesCommon>(): EditorData<Values>;
96
+
97
+ /**
98
+ * Type of the onChange function.
99
+ */
100
+ export declare type OnChange<Values extends ValuesCommon> = (values: Partial<Values>) => void;
101
+
102
+ /**
103
+ * The onChange function itself with 'origin' provided as a param.
104
+ *
105
+ * @param origin - web content's origin (URL) of your Dev Portal to send changes to
106
+ * @param instanceId - ID of this particular instance of the widget
107
+ * @param values - values that changed
108
+ */
109
+ export declare function onChangeWithOrigin<Values extends ValuesCommon>(origin: string, instanceId: string, values: Values): void;
110
+
111
+ /** Information about the widget instance received from the Dev Portal */
112
+ export declare interface PortalData {
113
+ /** current runtime environment */
114
+ environment: Environment;
115
+ /** ID of this particular instance of the widget */
116
+ instanceId: string;
117
+ }
118
+
119
+ /**
120
+ * Secrets needed for communication with Dev Portal back-end
121
+ */
122
+ export declare type Secrets = {
123
+ managementApiUrl: string;
124
+ apiVersion: string;
125
+ userId?: string;
126
+ token?: string;
127
+ };
128
+
129
+ /**
130
+ * resourceId - resource ID of API Management service "subscriptions/[subscription-id]/resourceGroups/[resource-group-name]/providers/Microsoft.ApiManagement/service/[service-name]"
131
+ * managementApiEndpoint - URL with protocol (e.g. https://management.azure.com)
132
+ * apiVersion - optional to override default (e.g. "2019-01-01")
133
+ * tokenOverride - optional, provides token to use for auth, instead of 'az login' approach
134
+ */
135
+ export declare type ServiceInformation = {
136
+ resourceId: string;
137
+ managementApiEndpoint: string;
138
+ apiVersion?: string;
139
+ tokenOverride?: string;
140
+ };
141
+
142
+ /**
143
+ * Possible target modules
144
+ * "app" for main application which is embedded in your Dev Portal
145
+ * "editor" for form in admin panel
146
+ */
147
+ export declare type TargetModule = "app" | "editor";
148
+
149
+ /**
150
+ * Base of a values obj
151
+ */
152
+ export declare type ValuesCommon = Record<string, unknown>;
153
+
154
+ export { }