pptb-standard-sample-tool 1.5.2 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -120,7 +120,7 @@ html-sample/
120
120
  - FetchXML query to retrieve top 10 accounts
121
121
  - Display results with formatting
122
122
  - If a FetchXML is saved in Tool Settings, the Query button will use that instead of the default
123
- - This sample supports multi-connection: try the "Secondary" buttons to run the same queries against the secondary connection
123
+ - This sample supports up to three connections: use the Primary, Secondary, and Third query buttons to test slots 0, 1, and 2
124
124
 
125
125
  **CRUD Operations:**
126
126
 
package/dist/app.js CHANGED
@@ -19,6 +19,7 @@ const powerplatform = window.powerplatformAPI;
19
19
  // Application state
20
20
  let currentConnection = null;
21
21
  let secondaryConnection = null;
22
+ let thirdConnection = null;
22
23
  let currentTerminal = null;
23
24
  let createdId = null;
24
25
  let currentLaunchContext = null;
@@ -137,7 +138,8 @@ async function identifyLaunchContext() {
137
138
  async function refreshConnection() {
138
139
  try {
139
140
  currentConnection = await toolbox.connections.getActiveConnection();
140
- secondaryConnection = await toolbox.connections.getSecondaryConnection();
141
+ secondaryConnection = await toolbox.connections.getConnection("secondary");
142
+ thirdConnection = await toolbox.connections.getConnection(2);
141
143
  const connectionInfo = document.getElementById("connection-info");
142
144
  if (!connectionInfo)
143
145
  return;
@@ -207,6 +209,39 @@ async function refreshConnection() {
207
209
  secondaryInfo.innerHTML = "<p><strong>⚠️ No secondary connection</strong><br>Please connect to a secondary Dataverse environment to use this tool.</p>";
208
210
  log("No secondary connection found", "warning");
209
211
  }
212
+ const thirdInfo = document.getElementById("third-connection-info");
213
+ if (!thirdInfo)
214
+ return;
215
+ if (thirdConnection) {
216
+ const envClass = thirdConnection.environment.toLowerCase();
217
+ thirdInfo.className = "info-box success";
218
+ thirdInfo.innerHTML = `
219
+ <div class="connection-details">
220
+ <div class="connection-item">
221
+ <strong>Name:</strong>
222
+ <span>${thirdConnection.name}</span>
223
+ </div>
224
+ <div class="connection-item">
225
+ <strong>URL:</strong>
226
+ <span>${thirdConnection.url}</span>
227
+ </div>
228
+ <div class="connection-item">
229
+ <strong>Environment:</strong>
230
+ <span class="env-badge ${envClass}">${thirdConnection.environment}</span>
231
+ </div>
232
+ <div class="connection-item">
233
+ <strong>ID:</strong>
234
+ <span>${thirdConnection.id}</span>
235
+ </div>
236
+ </div>
237
+ `;
238
+ log(`Third connection: ${thirdConnection.name}`, "success");
239
+ }
240
+ else {
241
+ thirdInfo.className = "info-box warning";
242
+ thirdInfo.innerHTML = "<p><strong>⚠️ No third connection</strong><br>Please connect a third Dataverse environment to use this tool.</p>";
243
+ log("No third connection found", "warning");
244
+ }
210
245
  }
211
246
  catch (error) {
212
247
  log(`Error refreshing connection: ${error.message}`, "error");
@@ -273,6 +308,7 @@ function setupEventHandlers() {
273
308
  // Dataverse query button
274
309
  document.getElementById("query-accounts-btn")?.addEventListener("click", queryAccounts);
275
310
  document.getElementById("query-accounts-secondary-btn")?.addEventListener("click", queryAccountsSecondary);
311
+ document.getElementById("query-accounts-third-btn")?.addEventListener("click", queryAccountsThird);
276
312
  document.getElementById("query-contacts-querydata-btn")?.addEventListener("click", queryContactQueryData);
277
313
  // CRUD buttons
278
314
  document.getElementById("create-contact-btn")?.addEventListener("click", createContact);
@@ -284,6 +320,9 @@ function setupEventHandlers() {
284
320
  document.getElementById("get-metadata-allentities")?.addEventListener("click", getAllEntities);
285
321
  //Execute buttons
286
322
  document.getElementById("whoami-btn")?.addEventListener("click", executeWhoAmI);
323
+ document.getElementById("query-with-headers-btn")?.addEventListener("click", queryAccountsWithHeaders);
324
+ document.getElementById("execute-batch-btn")?.addEventListener("click", executeReadBatch);
325
+ document.getElementById("execute-transaction-btn")?.addEventListener("click", executeAtomicTransaction);
287
326
  // Power Platform API buttons
288
327
  document.getElementById("list-apps-btn")?.addEventListener("click", listPowerApps);
289
328
  document.getElementById("list-flows-btn")?.addEventListener("click", listPowerAutomateFlows);
@@ -642,6 +681,53 @@ async function queryAccountsSecondary() {
642
681
  log(`Error querying accounts (secondary): ${error.message}`, "error");
643
682
  }
644
683
  }
684
+ /**
685
+ * Query accounts from Dataverse using the third connection slot (FetchXML)
686
+ */
687
+ async function queryAccountsThird() {
688
+ if (!thirdConnection) {
689
+ await showNotification("No Third Connection", "Please configure a third Dataverse connection", "warning");
690
+ return;
691
+ }
692
+ const output = document.getElementById("query-output-third");
693
+ try {
694
+ if (output)
695
+ output.textContent = "Querying accounts using third connection...\n";
696
+ const saved = await getSetting("demo.fetchxml");
697
+ const fetchXml = saved && saved.trim().length > 0
698
+ ? saved
699
+ : `
700
+ <fetch top="10">
701
+ <entity name="account">
702
+ <attribute name="name" />
703
+ <attribute name="accountid" />
704
+ <attribute name="emailaddress1" />
705
+ <attribute name="telephone1" />
706
+ <order attribute="name" />
707
+ </entity>
708
+ </fetch>
709
+ `.trim();
710
+ const result = await dataverse.fetchXmlQuery(fetchXml, 2);
711
+ if (output) {
712
+ output.textContent = `Found ${result.value.length} account(s) on third connection:\n\n`;
713
+ result.value.forEach((account, index) => {
714
+ output.textContent += `${index + 1}. ${account.name}\n`;
715
+ output.textContent += ` ID: ${account.accountid}\n`;
716
+ if (account.emailaddress1)
717
+ output.textContent += ` Email: ${account.emailaddress1}\n`;
718
+ if (account.telephone1)
719
+ output.textContent += ` Phone: ${account.telephone1}\n`;
720
+ output.textContent += "\n";
721
+ });
722
+ }
723
+ log(`Queried ${result.value.length} accounts on third connection`, "success");
724
+ }
725
+ catch (error) {
726
+ if (output)
727
+ output.textContent = `Error (third connection): ${error.message}`;
728
+ log(`Error querying accounts (third connection): ${error.message}`, "error");
729
+ }
730
+ }
645
731
  /**
646
732
  * Create a new account
647
733
  */
@@ -854,6 +940,98 @@ async function executeWhoAmI() {
854
940
  log(`Error executing WhoAmI: ${error.message}`, "error");
855
941
  }
856
942
  }
943
+ function getAdditionalHeaders() {
944
+ const input = document.getElementById("dataverse-headers");
945
+ const parsed = JSON.parse(input?.value || "{}");
946
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
947
+ throw new Error("Additional headers must be a JSON object");
948
+ }
949
+ const headers = {};
950
+ for (const [name, value] of Object.entries(parsed)) {
951
+ if (typeof value !== "string") {
952
+ throw new Error(`Header value for '${name}' must be a string`);
953
+ }
954
+ headers[name] = value;
955
+ }
956
+ return headers;
957
+ }
958
+ async function queryAccountsWithHeaders() {
959
+ if (!currentConnection) {
960
+ await showNotification("No Connection", "Please connect to a Dataverse environment", "warning");
961
+ return;
962
+ }
963
+ const output = document.getElementById("headers-output");
964
+ try {
965
+ const headers = getAdditionalHeaders();
966
+ if (output)
967
+ output.textContent = "Querying accounts with additional headers...\n";
968
+ const result = await dataverse.queryData("accounts?$select=name,accountid&$top=5", "primary", headers);
969
+ if (output) {
970
+ output.textContent = `Found ${result.value.length} account(s):\n\n`;
971
+ output.textContent += JSON.stringify(result.value, null, 2);
972
+ }
973
+ log(`Queried ${result.value.length} accounts with additional headers`, "success");
974
+ }
975
+ catch (error) {
976
+ if (output)
977
+ output.textContent = `Error: ${error.message}`;
978
+ log(`Error querying accounts with headers: ${error.message}`, "error");
979
+ }
980
+ }
981
+ async function executeReadBatch() {
982
+ if (!currentConnection) {
983
+ await showNotification("No Connection", "Please connect to a Dataverse environment", "warning");
984
+ return;
985
+ }
986
+ const output = document.getElementById("batch-output");
987
+ try {
988
+ const headers = getAdditionalHeaders();
989
+ if (output)
990
+ output.textContent = "Executing read batch...\n";
991
+ const results = await dataverse.executeBatch([
992
+ { method: "GET", url: "accounts?$select=name,accountid&$top=3" },
993
+ { method: "GET", url: "contacts?$select=fullname,contactid&$top=3" },
994
+ ], "primary", headers);
995
+ if (output)
996
+ output.textContent = `Batch returned ${results.length} result(s):\n\n${JSON.stringify(results, null, 2)}`;
997
+ log(`Read batch completed with ${results.length} result(s)`, "success");
998
+ }
999
+ catch (error) {
1000
+ if (output)
1001
+ output.textContent = `Error: ${error.message}`;
1002
+ log(`Error executing read batch: ${error.message}`, "error");
1003
+ }
1004
+ }
1005
+ async function executeAtomicTransaction() {
1006
+ if (!currentConnection) {
1007
+ await showNotification("No Connection", "Please connect to a Dataverse environment", "warning");
1008
+ return;
1009
+ }
1010
+ const output = document.getElementById("batch-output");
1011
+ try {
1012
+ const headers = getAdditionalHeaders();
1013
+ const suffix = new Date().toISOString();
1014
+ if (output)
1015
+ output.textContent = `Executing atomic transaction (creating an account and contact - ${`HTML Sample ${suffix}`} )...\n`;
1016
+ const results = await dataverse.executeTransaction([
1017
+ { method: "POST", url: "accounts", contentId: "account", body: { name: `HTML Sample ${suffix}` } },
1018
+ {
1019
+ method: "POST",
1020
+ url: "contacts",
1021
+ contentId: "contact",
1022
+ body: { firstname: "HTML", lastname: `Sample ${suffix}` },
1023
+ },
1024
+ ], "primary", headers);
1025
+ if (output)
1026
+ output.textContent = `Transaction returned ${results.length} result(s):\n\n${JSON.stringify(results, null, 2)}`;
1027
+ log(`Atomic transaction completed with ${results.length} result(s)`, "success");
1028
+ }
1029
+ catch (error) {
1030
+ if (output)
1031
+ output.textContent = `Error: ${error.message}`;
1032
+ log(`Error executing atomic transaction: ${error.message}`, "error");
1033
+ }
1034
+ }
857
1035
  /**
858
1036
  * Apply current theme
859
1037
  */
package/dist/index.html CHANGED
@@ -29,6 +29,13 @@
29
29
  </div>
30
30
  </section>
31
31
 
32
+ <section class="card">
33
+ <h2>🔗 Third Connection Status</h2>
34
+ <div id="third-connection-info" class="info-box">
35
+ <div class="loading">Checking connection...</div>
36
+ </div>
37
+ </section>
38
+
32
39
  <!-- ToolBox API Examples -->
33
40
  <section class="card">
34
41
  <h2>🛠️ ToolBox API Examples</h2>
@@ -146,10 +153,12 @@
146
153
  <div class="button-group">
147
154
  <button id="query-accounts-btn" class="btn btn-primary">Query Top 10 Accounts (Primary)</button>
148
155
  <button id="query-accounts-secondary-btn" class="btn">Query Top 10 Accounts (Secondary)</button>
156
+ <button id="query-accounts-third-btn" class="btn">Query Top 10 Accounts (Third)</button>
149
157
  <button id="query-contacts-querydata-btn" class="btn">Query Top 10 Contacts (Primary)</button>
150
158
  </div>
151
159
  <div id="query-output" class="output"></div>
152
160
  <div id="query-output-secondary" class="output"></div>
161
+ <div id="query-output-third" class="output"></div>
153
162
  </div>
154
163
 
155
164
  <div class="example-group">
@@ -183,6 +192,38 @@
183
192
  <button id="whoami-btn" class="btn">Execute WhoAmI</button>
184
193
  <div id="execute-output" class="output"></div>
185
194
  </div>
195
+
196
+ <div class="example-group">
197
+ <h3>Additional Headers</h3>
198
+ <p style="margin: 4px 0 10px; font-size: 12px; opacity: 0.85">
199
+ Headers are shown for consent before the request is sent. Use valid Dataverse header names and string values.
200
+ </p>
201
+ <div class="input-group">
202
+ <label for="dataverse-headers">Additional Headers (JSON):</label>
203
+ <textarea
204
+ id="dataverse-headers"
205
+ style="width: 100%; min-height: 90px; padding: 10px; border: 2px solid #e0e0e0; border-radius: 6px; font-family: monospace; font-size: 12px; line-height: 1.4"
206
+ >{
207
+ "Prefer": "odata.include-annotations=\"*\""
208
+ }</textarea>
209
+ </div>
210
+ <div class="button-group">
211
+ <button id="query-with-headers-btn" class="btn btn-primary">Query Accounts with Headers</button>
212
+ </div>
213
+ <div id="headers-output" class="output"></div>
214
+ </div>
215
+
216
+ <div class="example-group">
217
+ <h3>Batch Operations</h3>
218
+ <p style="margin: 4px 0 10px; font-size: 12px; opacity: 0.85">
219
+ Runs independent reads in one <code>$batch</code> request or creates an account and contact in one atomic transaction.
220
+ </p>
221
+ <div class="button-group">
222
+ <button id="execute-batch-btn" class="btn btn-primary">Execute Read Batch</button>
223
+ <button id="execute-transaction-btn" class="btn btn-warning">Execute Atomic Transaction</button>
224
+ </div>
225
+ <div id="batch-output" class="output"></div>
226
+ </div>
186
227
  </section>
187
228
 
188
229
  <!-- Power Platform API Examples -->
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "pptb-standard-sample-tool",
3
- "version": "1.5.1",
3
+ "version": "1.7.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pptb-standard-sample-tool",
9
- "version": "1.5.1",
9
+ "version": "1.7.0",
10
10
  "license": "GPL-3.0",
11
11
  "devDependencies": {
12
- "@pptb/types": "^1.2.6-beta.2",
12
+ "@pptb/types": "^1.2.7-beta.4",
13
13
  "shx": "^0.4.0",
14
14
  "typescript": "^5.0.0"
15
15
  },
@@ -69,26 +69,26 @@
69
69
  }
70
70
  },
71
71
  "node_modules/@pptb/types": {
72
- "version": "1.2.6-beta.2",
73
- "resolved": "https://registry.npmjs.org/@pptb/types/-/types-1.2.6-beta.2.tgz",
74
- "integrity": "sha512-k6Ov2Jfl3c2Et4h86fiKPZK2E0i/hIMGDNnXsXZgREAwU4tMtBmcqrQNr6YfFJOIRzM2fSbQDqW9bGecup5i2Q==",
72
+ "version": "1.2.7-beta.4",
73
+ "resolved": "https://registry.npmjs.org/@pptb/types/-/types-1.2.7-beta.4.tgz",
74
+ "integrity": "sha512-i/bkvXuplSbOHgekfQglmuGDc2PrBs+3uX/rFdNOl1RhyAf1st4SbseA/YeA2eWQN2Mm32YAXhuGqB7BS0yxdw==",
75
75
  "dev": true,
76
76
  "license": "GPL-3.0",
77
77
  "dependencies": {
78
- "@pptb/validate": "1.0.2"
78
+ "@pptb/validate": "1.0.4"
79
79
  },
80
80
  "bin": {
81
81
  "pptb-validate": "bin/pptb-validate.js"
82
82
  }
83
83
  },
84
84
  "node_modules/@pptb/validate": {
85
- "version": "1.0.2",
86
- "resolved": "https://registry.npmjs.org/@pptb/validate/-/validate-1.0.2.tgz",
87
- "integrity": "sha512-D6bfEa1a7p9ju7BUY2l/jJ8tK7s5SEtpvNriBRfasazP91mJ6eL5mLqOGwwjQ9aPyRJoeFyVmdF4gRyFMCcG7Q==",
85
+ "version": "1.0.4",
86
+ "resolved": "https://registry.npmjs.org/@pptb/validate/-/validate-1.0.4.tgz",
87
+ "integrity": "sha512-Xew21gUgwrdstVpu6EB8b8vDaWNE4OOiPqr1Xk61Ao2EKN4ioCVg/cG6uFRFBhLaLqSjublVYczJ01KAUbBsvw==",
88
88
  "dev": true,
89
89
  "license": "GPL-3.0",
90
90
  "dependencies": {
91
- "tar": "^7.5.16"
91
+ "tar": "^7.5.21"
92
92
  },
93
93
  "bin": {
94
94
  "pptb-validate": "dist/cli.js"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pptb-standard-sample-tool",
3
- "version": "1.5.2",
3
+ "version": "1.7.0",
4
4
  "displayName": "HTML Sample Tool",
5
5
  "description": "A sample Power Platform ToolBox tool built with HTML, CSS, and TypeScript",
6
6
  "main": "index.html",
@@ -27,10 +27,12 @@
27
27
  "readmeUrl": "https://raw.githubusercontent.com/PowerPlatformToolBox/sample-tools/refs/heads/main/new/html-sample/README.md"
28
28
  },
29
29
  "features": {
30
- "minAPI": "1.2.6",
31
- "multiConnection": "optional",
30
+ "minAPI": "1.2.7",
32
31
  "enabledForPowerPlatformAPI": true,
33
- "connectionRequirement": "optional"
32
+ "connections": {
33
+ "min": 0,
34
+ "max": 3
35
+ }
34
36
  },
35
37
  "cspExceptions": {
36
38
  "style-src": [
@@ -79,7 +81,7 @@
79
81
  "validate": "pptb-validate"
80
82
  },
81
83
  "devDependencies": {
82
- "@pptb/types": "^1.2.6-beta.2",
84
+ "@pptb/types": "^1.2.7-beta.4",
83
85
  "shx": "^0.4.0",
84
86
  "typescript": "^5.0.0"
85
87
  },