diff --git a/docs/images/working-set-check.png b/docs/images/working-set-check.png new file mode 100644 index 0000000..ea81d38 Binary files /dev/null and b/docs/images/working-set-check.png differ diff --git a/docs/native-embedding-architecture/apply-filter.md b/docs/native-embedding-architecture/apply-filter.md index 26cb03e..fb3fdd6 100644 --- a/docs/native-embedding-architecture/apply-filter.md +++ b/docs/native-embedding-architecture/apply-filter.md @@ -367,6 +367,25 @@ To apply selections on an Attribute/Metric selector inside a page, you can use t } ``` +##### Value parameter filter + +To apply values for a value parameter filter, you can use the following input: + +```js +try { + await mstrDossier.applyFilter({ + key: "W76", + currentSelection: { + value: "Profit", + }, + }); +} catch (error) { + // Your own error handling code +} +``` + +Then the value parameter's value changes, which affects all the visualizations that use the related value parameter. + #### Visualizations used as filters - Select attribute elements of the visualization diff --git a/docs/native-embedding-architecture/mstr-environment.md b/docs/native-embedding-architecture/mstr-environment.md index 324f646..01e2d0a 100644 --- a/docs/native-embedding-architecture/mstr-environment.md +++ b/docs/native-embedding-architecture/mstr-environment.md @@ -13,18 +13,24 @@ The instance of this class is the object returned from the `microstrategy.embedd #### Function -`async loadDossier(props)` +`async loadDossier(props, options)` #### Input Parameters -| Parameter Name | Data Type | Description | Is Required | -| ------------------- | --------- | -------------------------------------------------------------------------------------------------------- | ----------- | -| props.projectId | String | The project ID, which must be a GUID. | true | -| props.objectId | String | The dashboard ID, which must be valid. If the ID is a document, report, or bot ID, an error is reported. | true | -| props.instanceId | String | The dashboard instance ID, if it already exists. | false | -| props.applicationId | String | the dashboard application ID, if not specified, the default application will be used | false | +| Parameter Name | Data Type | Description | Is Required | +| ------------------------------ | --------- | ---------------------------------------------------------------------------------------------------------------------------- | ----------- | +| props.projectId | String | The project ID, which must be a GUID. | true | +| props.objectId | String | The dashboard ID, which must be valid. If the ID is a document, report, or bot ID, an error is reported. | true | +| props.instanceId | String | The dashboard instance ID, if it already exists. | false | +| props.bookmarkId | String | The ID of a bookmark that is owned by this dossier, if it is provided. | false | +| props.applicationId | String | the dashboard application ID, if not specified, the default application will be used | false | +| options.forceCreateNewInstance | Boolean | Whether to create a new dossier instance forcefully if the other MstrDossier object is already created based on this dossier | false | -The `projectId` + `objectId` is used as the dashboard identifier. If the function is called twice with the same parameter, the same `MstrDossier` object is returned in the callback. +The `projectId` + `objectId` is used as the dashboard identifier. +If the function is called twice with the same the dashboard identifier: + +- If `options.forceCreateNewInstance` is not set or false, the same `MstrDossier` object is returned in the callback. +- If `options.forceCreateNewInstance` is true, a new `MstrDossier` object that holds a new instance is returned in the callback. #### Response @@ -41,10 +47,15 @@ try { }, }); // Begin here - const dossier = await environment.loadDossier({ - projectId: "B19DEDCC11D4E0EFC000EB9495D0F44F", - objectId: "D9AB379D11EC92C1D9DC0080EFD415BB", - }); + const dossier = await environment.loadDossier( + { + projectId: "B19DEDCC11D4E0EFC000EB9495D0F44F", + objectId: "D9AB379D11EC92C1D9DC0080EFD415BB", + }, + { + forceCreateNewInstance: true, + } + ); } catch (error) { // Your own error handling logic } diff --git a/docs/native-embedding-architecture/multiple-instances-for-one-dossier.md b/docs/native-embedding-architecture/multiple-instances-for-one-dossier.md new file mode 100644 index 0000000..c693695 --- /dev/null +++ b/docs/native-embedding-architecture/multiple-instances-for-one-dossier.md @@ -0,0 +1,150 @@ +--- +title: Create multiple instances for one dashboard +description: The user can use Native Embedding SDK API to create multiple instances for one dashboard. +--- + + + +### One dashboard owns only one instance + +In the normal case, the Native Embedding SDK only creates one `MstrDossier` object for one dashboard object. That is, if you write code like this: + +```js +const mstrEnvironment = await microstrategy.embeddingComponent.environments.create({ + serverUrl: configs.serverUrl, + getAuthToken, +}); +const mstrDossierA = await mstrEnvironment.loadDossier({ + projectId: configs.projectId, + objectId: configs.objectId, +}); +// mstrDossierB === mstrDossierA +const mstrDossierB = await mstrEnvironment.loadDossier({ + projectId: configs.projectId, + objectId: configs.objectId, +}); +``` + +Then `mstrDossierB` will be the same as `mstrDossierA`. They are exactly the same object and share the same dashboard instance. For one dashboard instance, if it renders the visualizations from different pages, like: + +```js +await mstrDossierA.refresh([ + { + key: "K52", // Page 1, viz 1 + container: document.getElementById("container1"), + }, + { + key: "W63", // Page 1, viz 2 + container: document.getElementById("container2"), + }, + { + key: "W64", // Page 2, viz 3 + container: document.getElementById("container3"), + }, + { + key: "R85", // Page 2, viz 4 + container: document.getElementById("container4"), + }, +]); +``` + +The dashboard will first fetch the page 1 data, render the visualization 1 and 2, and then fetch the page 2 data and render visualization 3 and 4. + +This brings a problem: If the user renders the visualizations from many different pages, the visualizations on different pages will render sequentially, which downgrades the performance. + +### One dashboard owns only multiple instances + +In Strategy ONE December 2025, we support one dossier to have multiple instances, which can render the visualizations from different pages in parallel, and accelerate the rendering speed. The point is, for each page, we can create a `MstrDossier` object that owns an independent dashboard instance, and these instances can be created and fetched data simultaneously. + +We need to use a new param `forceCreateNewInstance` in the [loadDossier()](./mstr-environment#the-load-dashboard-api) function to do this: + +```js +const environment = await microstrategy.embeddingComponent.environments.create({ + serverUrl, + getAuthToken, +}); + +async function refreshDossier(vizAndContainers) { + const mstrDossier = await environment.loadDossier( + { + projectId, + objectId, + }, + { + forceCreateNewInstance: true, + } + ); + await mstrDossier.refresh(vizAndContainers); + return mstrDossier; +} + +// Here mstrDossierA !== mstrDossierB +const [mstrDossierA, mstrDossierB] = await Promise.all([ + refreshDossier([ + { + key: "K52", // Page 1, viz 1 + container: document.getElementById("container1"), + }, + { + key: "W63", // Page 1, viz 2 + container: document.getElementById("container2"), + }, + ]), + refreshDossier([ + { + key: "W64", // Page 2, viz 1 + container: document.getElementById("container3"), + }, + { + key: "R85", // Page 2, viz 2 + container: document.getElementById("container4"), + }, + ]), +]); +``` + +Here `mstrDossierA !== mstrDossierB`. The 2 `refreshDossier()` functions can be executed in parallel, which improves the rendering speed. + +### Change the working set limit + +On the Strategy iServer, the dashboard instance count limit for each user session is 10 by default. So if the embedding SDK user creates too many dashboard instances on a web page, the former instances might be expired or deleted. + +To solve this problem, the user can change the working set limit to a larger number. This could be done by assigning this limit by `workingSet` parameter in the [login API](https://demo.microstrategy.com/MicroStrategyLibrary/api-docs/index.html#/Authentication/postLogin) like: + +```js +const mstrEnvironment = await microstrategy.embeddingComponent.environments.create({ + serverUrl: configs.serverUrl, + getAuthToken: () => { + const options = { + method: "POST", + credentials: "include", + mode: "cors", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ + loginMode: 1, + username: "XXX", + password: "YYY", + // Enlarge the working set limit + workingSet: 50, + }), + }; + return fetch(`${configs.serverUrl}/api/auth/login`, options) + .then((response) => { + if (response.ok) { + console.log("A new standard login session has been created successfully, logging in"); + return response.headers.get("x-mstr-authtoken"); + } + return response.json().then((json) => console.log(json)); + }) + .catch((error) => console.error("Failed Standard Login with error:", error)); + }, +}); +``` + +For the other type of authentication methods, like OIDC, you can set the `workingSet` number in the URL query parameter. + +### Verify whether the working set limit change succeeds + +If you want to check whether your working set limit change is successful, you can open the dev tool and check the GET /api/sessions REST API result: +![workingSet](../images/working-set-check.png) +If the `workingSet` number in the REST API response is the same as the number you set, you setting works in the current session. diff --git a/docs/native-embedding-architecture/use-bookmark.md b/docs/native-embedding-architecture/use-bookmark.md new file mode 100644 index 0000000..c6163ab --- /dev/null +++ b/docs/native-embedding-architecture/use-bookmark.md @@ -0,0 +1,234 @@ +--- +title: Manage bookmarks on a dashboard +description: The user can use Native Embedding SDK API to get the bookmark list of a dashboard. +--- + + + +The user can use bookmarks to store multiple pieces of copies for one dashboard object. These bookmarks are independent, so the manipulations on one bookmark will not affect another. + +For example, if you have the requirements to show a visualization in one dossier multiple times on your web page, and these visualizations should have different filters, you can create a bookmark for each visualization copy. Here is an example: + +## Example Case + +### Embed a normal visualization + +At start, the user uses the old APIs to create an environment and load a dossier, then embed some visualizations: + +```js +const mstrEnvironment = await microstrategy.embeddingComponent.environments.create({ + serverUrl: configs.serverUrl, + hasExtraCheckBeforeApiCall: false, + getAuthToken, +}); +const mstrDossier = await mstrEnvironment.loadDossier({ + projectId: configs.projectId, + objectId: configs.objectId, +}); +await mstrDossier.refresh([ + { + key: "W65", + container: document.getElementById("container1"), + }, +]); +``` + +### Copy the visualization to another container + +After the visualization is embedded, the user can use a “copy“ command (Might be invoked by some menu implemented by the user) to execute the APIs + +```js +const { id } = await mstrDossier.createNewBookmark({ + name: "Visualization A copy 1", +}); +const mstrDossierCopy = await mstrEnvironment.loadDossier({ + projectId, + dossierId, + bookmarkId: id, +}); +await mstrDossierCopy.refresh([ + { + key: "W65", + container: document.getElementById("container2"), + }, +]); +``` + +Now we can see the original visualization `W65` is in "container1" and the copied visualization `W65` is in "container2". We can apply different filter content to the 2 visualizations as below. On one visualization, the user can do manipulations, which will not affect the other visualization. + +### Apply different filters to different visualization copies + +```js +// These 2 APIs can execute at the same time. No need to await the 1st API call. +mstrDossier.applyFilter({ + key: "W181", + currentSelection: { + elements: [ + { + id: "h6;8D679D4511D3E4981000E787EC6DE8A4", + name: "June", + }, + ], + }, +}); +mstrDossierCopy.applyFilter({ + key: "W181", + currentSelection: { + elements: [ + { + id: "h4;8D679D4511D3E4981000E787EC6DE8A4", + name: "April", + }, + ], + }, +}); +``` + +The "Copy" process in steps 2-3 can be done for multiple times, and can be based on the original visualization or the copied visualization. + +### Reload the visualization copies by bookmarks + +The user needs to store the bookmark IDs for the copied visualizations. If the page is refreshed, the user needs to call the following APIs to restore all the visualization’s latest status. Please note that all the bookmarks' instances can be created in parallel: + +```js +const mstrEnvironment = await microstrategy.embeddingComponent.environments.create({ + serverUrl: configs.serverUrl, + hasExtraCheckBeforeApiCall: false, + getAuthToken, +}); + +async function showOriginalVisualization(containerId) { + const mstrDossier = await mstrEnvironment.loadDossier({ + projectId, + dossierId, + }); + await mstrDossier.refresh([ + { + key: "W65", + container: document.getElementById(containerId), + }, + ]); + return mstrDossier; +} + +async function showCopiedVisualization(bookmarkId, containerId) { + const mstrDossierCopy = await mstrEnvironment.loadDossier({ + projectId, + dossierId, + bookmarkId, + }); + await mstrDossierCopy.refresh([ + { + key: "W65", + container: document.getElementById(containerId), + }, + ]); + return mstrDossierCopy; +} + +const mstrDossiers = await Promise.all([ + showOriginalVisualization("container1"), + showCopiedVisualization(bookmarkIdA, "container2"), + showCopiedVisualization(bookmarkIdB, "container3"), + showCopiedVisualization(bookmarkIdC, "container4"), +]); +``` + +## APIs + +### MstrDossier.createNewBookmark(props) + +#### Function + +`async MstrDossier.createNewBookmark(props)` + +#### Example + +```js +const bookmark = await mstrDossier.createNewBookmark({ + name: "Visualization A copy 1", +}); +``` + +#### New Input Parameters + +| New Parameter name | Data Type | Description | Default Value | Is Required | +| ------------------ | --------- | ---------------------- | ------------- | ----------- | +| props.name | string | The new bookmark name. | N/A | true | + +#### Response + +A Promise object that resolves to an object like: + +```json +{ + "id": "07002F50734DC255D70A37BA1962B3A5" +} +``` + +#### API Errors + +| Error case | Error handling | Error message | +| --------------------------------------------------------------------------------------- | -------------------------------- | --------------------------------------------------------------------------------------------------------------------- | +| The input parameter fails input validation: The bookmarkName isn't a string or is empty | The error object could be caught | The validation error | +| The dossier object doesn’t have a shortcut | The error object could be caught | “This dossier doesn’t have a shortcut, so it can’t be used to create a bookmark. Please add it to the Library first.” | +| The background REST API fails | Caught by custom error handler | The error message provided by the REST API | + +### MstrDossier.deleteBookmark() + +The user can use this API to get a full bookmark list of the dossier. It can be used for the initial loading. + +#### Function + +`async MstrDossier.deleteBookmark(bookmarkId)` + +#### Example + +```js +await mstrDossier.deleteBookmark(bookmarkId); +``` + +#### Response + +N/A + +#### API Errors + +| Error case | Error handling | Error message | +| ----------------------------- | ------------------------------ | ------------------------------------------ | +| The background REST API fails | Caught by custom error handler | The error message provided by the REST API | + +### MstrDossier.getAvailableBookmarkList() + +The user can use this API to get a full bookmark list of the dossier. It can be used for the initial loading. + +#### Function + +`async MstrDossier.getAvailableBookmarkList()` + +#### Example + +```js +const bookmarkList = await mstrDossier.getAvailableBookmarkList(); +``` + +#### Response + +```json +[ + { + "id": "35D095A8114E7C1469ED87B1F21533B9", + "name": "Visualization A copy 1" + }, + { + "id": "46D095A8114E7C1469ED87B1F21533B9", + "name": "Visualization A copy 2" + } +] +``` + +#### API Errors + +| Error case | Error handling | Error message | +| ----------------------------- | ------------------------------ | ------------------------------------------ | +| The background REST API fails | Caught by custom error handler | The error message provided by the REST API | diff --git a/docs/whats-new-in-the-embedding-sdk.md b/docs/whats-new-in-the-embedding-sdk.md index cfdf75f..e112388 100644 --- a/docs/whats-new-in-the-embedding-sdk.md +++ b/docs/whats-new-in-the-embedding-sdk.md @@ -5,6 +5,16 @@ description: In each release, changes are made to make the MicroStrategy SDK mor In each release, changes are made to make the MicroStrategy SDK more powerful and easier to use. +## Strategy ONE December 2025 + +- [Support multiple instances for one dashboard](./native-embedding-architecture/multiple-instances-for-one-dossier) + - Use the new `forceCreateNewInstance` parameter in the [MstrEnvironment.loadDossier()](./native-embedding-architecture/mstr-environment#the-load-dashboard-api) API to support creating multiple instances for one dashboard in Native Embedding SDK. +- [Support using bookmarks to show multiple copies of one visualization](./native-embedding-architecture/use-bookmark) + - Support to use bookmark APIs to create, delete, and get bookmarks; + - Support using `bookmarkId` to load a `MstrDossier` object based on a bookmark in the [MstrEnvironment.loadDossier()](./native-embedding-architecture/mstr-environment#the-load-dashboard-api) API. +- [Support applying values for value parameter filter](./native-embedding-architecture/apply-filter#value-parameter-filter) + - Support the value parameter filter in the `MstrDossier.applyFilter()` and `MstrDossier.applyFilters()` API. + ## Strategy ONE September 2025 - [Support dataset ids and pause mode in embedded authoring](./add-functionality/authoring-library#api-for-entering-authoring-mode-or-disabling-authoring-mode-in-the-initial-loading) diff --git a/sidebars.js b/sidebars.js index ac14319..2b30415 100644 --- a/sidebars.js +++ b/sidebars.js @@ -144,6 +144,8 @@ const sidebars = { "native-embedding-architecture/apply-filter", "native-embedding-architecture/event-handling", "native-embedding-architecture/information-window", + "native-embedding-architecture/use-bookmark", + "native-embedding-architecture/multiple-instances-for-one-dossier", "native-embedding-architecture/feature-flags", ], },