@microsoft/power-apps-native-host 0.4.0 → 0.5.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/README.md +60 -0
- package/compatibility/current.json +1 -1
- package/lib/NOTICE.txt +2957 -371
- package/lib/commonjs/auth/AuthContext.js +49 -22
- package/lib/commonjs/auth/targetAuthorization.js +53 -0
- package/lib/commonjs/bridge/NativePowerAppsBridge.js +33 -2
- package/lib/commonjs/bridge/PowerPagesBridge.js +1 -3
- package/lib/commonjs/bridge/setupNativeHost.js +6 -0
- package/lib/commonjs/config/expoConfig.js +14 -0
- package/lib/commonjs/config/runtimeTenantMode.js +16 -0
- package/lib/commonjs/config/runtimeTenantMode.web.js +9 -0
- package/lib/commonjs/config/tenantMode.js +68 -0
- package/lib/commonjs/connections/PowerPlatformConnectionApi.js +24 -27
- package/lib/commonjs/connections/connectionMetadata.js +2 -2
- package/lib/commonjs/connections/resolveConnections.js +8 -3
- package/lib/commonjs/connections/types.js +9 -2
- package/lib/commonjs/connections/useConnectionRefs.js +19 -10
- package/lib/commonjs/index.js +39 -0
- package/lib/commonjs/platform/CanvasAppResolution.js +33 -0
- package/lib/commonjs/platform/CanvasAppResolutionApi.js +69 -0
- package/lib/commonjs/platform/DataverseGlobalDiscoveryApi.js +50 -13
- package/lib/commonjs/platform/GlobalDiscovery.js +40 -1
- package/lib/commonjs/provider/PowerAppsHostProvider.js +47 -8
- package/lib/commonjs/provider/PowerAppsProvider.js +172 -6
- package/lib/commonjs/target/DefaultPowerAppsExperience.js +564 -0
- package/lib/commonjs/target/PowerAppsExperience.js +41 -0
- package/lib/commonjs/target/PowerAppsTargetBootstrap.js +534 -0
- package/lib/commonjs/target/errors.js +24 -0
- package/lib/commonjs/target/types.js +1 -0
- package/lib/module/auth/AuthContext.js +49 -22
- package/lib/module/auth/targetAuthorization.js +48 -0
- package/lib/module/bridge/NativePowerAppsBridge.js +33 -2
- package/lib/module/bridge/PowerPagesBridge.js +1 -3
- package/lib/module/bridge/setupNativeHost.js +5 -0
- package/lib/module/config/expoConfig.js +14 -0
- package/lib/module/config/runtimeTenantMode.js +12 -0
- package/lib/module/config/runtimeTenantMode.web.js +5 -0
- package/lib/module/config/tenantMode.js +61 -0
- package/lib/module/connections/PowerPlatformConnectionApi.js +24 -27
- package/lib/module/connections/connectionMetadata.js +2 -2
- package/lib/module/connections/resolveConnections.js +9 -4
- package/lib/module/connections/types.js +9 -2
- package/lib/module/connections/useConnectionRefs.js +20 -11
- package/lib/module/index.js +4 -1
- package/lib/module/platform/CanvasAppResolution.js +27 -0
- package/lib/module/platform/CanvasAppResolutionApi.js +65 -0
- package/lib/module/platform/DataverseGlobalDiscoveryApi.js +49 -14
- package/lib/module/platform/GlobalDiscovery.js +37 -1
- package/lib/module/provider/PowerAppsHostProvider.js +47 -8
- package/lib/module/provider/PowerAppsProvider.js +176 -10
- package/lib/module/target/DefaultPowerAppsExperience.js +559 -0
- package/lib/module/target/PowerAppsExperience.js +34 -0
- package/lib/module/target/PowerAppsTargetBootstrap.js +529 -0
- package/lib/module/target/errors.js +19 -0
- package/lib/module/target/types.js +1 -0
- package/lib/typescript/commonjs/auth/AuthContext.d.ts +11 -2
- package/lib/typescript/commonjs/auth/targetAuthorization.d.ts +15 -0
- package/lib/typescript/commonjs/bridge/NativePowerAppsBridge.d.ts +6 -0
- package/lib/typescript/commonjs/bridge/setupNativeHost.d.ts +2 -0
- package/lib/typescript/commonjs/config/runtimeTenantMode.d.ts +3 -0
- package/lib/typescript/commonjs/config/runtimeTenantMode.web.d.ts +3 -0
- package/lib/typescript/commonjs/config/tenantMode.d.ts +14 -0
- package/lib/typescript/commonjs/connections/PowerPlatformConnectionApi.d.ts +5 -2
- package/lib/typescript/commonjs/connections/connectionMetadata.d.ts +1 -1
- package/lib/typescript/commonjs/connections/types.d.ts +7 -1
- package/lib/typescript/commonjs/connections/useConnectionRefs.d.ts +11 -2
- package/lib/typescript/commonjs/index.d.ts +5 -1
- package/lib/typescript/commonjs/platform/CanvasAppResolution.d.ts +4 -0
- package/lib/typescript/commonjs/platform/CanvasAppResolutionApi.d.ts +6 -0
- package/lib/typescript/commonjs/platform/DataverseGlobalDiscoveryApi.d.ts +12 -2
- package/lib/typescript/commonjs/platform/GlobalDiscovery.d.ts +12 -0
- package/lib/typescript/commonjs/provider/PowerAppsHostProvider.d.ts +9 -1
- package/lib/typescript/commonjs/provider/PowerAppsProvider.d.ts +13 -15
- package/lib/typescript/commonjs/target/DefaultPowerAppsExperience.d.ts +6 -0
- package/lib/typescript/commonjs/target/PowerAppsExperience.d.ts +61 -0
- package/lib/typescript/commonjs/target/PowerAppsTargetBootstrap.d.ts +15 -0
- package/lib/typescript/commonjs/target/errors.d.ts +13 -0
- package/lib/typescript/commonjs/target/types.d.ts +34 -0
- package/lib/typescript/module/auth/AuthContext.d.ts +11 -2
- package/lib/typescript/module/auth/targetAuthorization.d.ts +15 -0
- package/lib/typescript/module/bridge/NativePowerAppsBridge.d.ts +6 -0
- package/lib/typescript/module/bridge/setupNativeHost.d.ts +2 -0
- package/lib/typescript/module/config/runtimeTenantMode.d.ts +3 -0
- package/lib/typescript/module/config/runtimeTenantMode.web.d.ts +3 -0
- package/lib/typescript/module/config/tenantMode.d.ts +14 -0
- package/lib/typescript/module/connections/PowerPlatformConnectionApi.d.ts +5 -2
- package/lib/typescript/module/connections/connectionMetadata.d.ts +1 -1
- package/lib/typescript/module/connections/types.d.ts +7 -1
- package/lib/typescript/module/connections/useConnectionRefs.d.ts +11 -2
- package/lib/typescript/module/index.d.ts +5 -1
- package/lib/typescript/module/platform/CanvasAppResolution.d.ts +4 -0
- package/lib/typescript/module/platform/CanvasAppResolutionApi.d.ts +6 -0
- package/lib/typescript/module/platform/DataverseGlobalDiscoveryApi.d.ts +12 -2
- package/lib/typescript/module/platform/GlobalDiscovery.d.ts +12 -0
- package/lib/typescript/module/provider/PowerAppsHostProvider.d.ts +9 -1
- package/lib/typescript/module/provider/PowerAppsProvider.d.ts +13 -15
- package/lib/typescript/module/target/DefaultPowerAppsExperience.d.ts +6 -0
- package/lib/typescript/module/target/PowerAppsExperience.d.ts +61 -0
- package/lib/typescript/module/target/PowerAppsTargetBootstrap.d.ts +15 -0
- package/lib/typescript/module/target/errors.d.ts +13 -0
- package/lib/typescript/module/target/types.d.ts +34 -0
- package/package.json +12 -5
- package/scripts/build-codegen-package.js +34 -3
- package/scripts/dev.js +405 -0
- package/scripts/upgrade-authoring.js +85 -28
- package/scripts/wrap.js +42 -5
- package/src/config/generated-config.d.ts +21 -0
|
@@ -23,8 +23,10 @@ var _connectionMetadata = require("./connectionMetadata.js");
|
|
|
23
23
|
var _disambiguate = require("../connections/disambiguate.js");
|
|
24
24
|
var _connectionState = require("./connectionState.js");
|
|
25
25
|
var _index = require("../telemetry/index.js");
|
|
26
|
+
var _powerAppsNativeCommon = require("@microsoft/power-apps-native-common");
|
|
26
27
|
var _http = require("@microsoft/power-apps-native-common/http");
|
|
27
28
|
var _logger = require("../logger.js");
|
|
29
|
+
var _errors = require("../target/errors.js");
|
|
28
30
|
/*!
|
|
29
31
|
* power-apps-native-host — PowerPlatformConnectionApi
|
|
30
32
|
*
|
|
@@ -135,8 +137,7 @@ async function fetchConnectionsForConnector(connectorId, environmentId, region,
|
|
|
135
137
|
}
|
|
136
138
|
});
|
|
137
139
|
if (!res.ok) {
|
|
138
|
-
|
|
139
|
-
throw new Error(`Unable to list connector connections (HTTP ${res.status}): ${errorText}`);
|
|
140
|
+
throw new Error(`Unable to list connector connections (HTTP ${res.status})`);
|
|
140
141
|
}
|
|
141
142
|
const body = await res.json();
|
|
142
143
|
return (body.value ?? []).map(c => {
|
|
@@ -157,7 +158,7 @@ async function fetchConnectionsForConnector(connectorId, environmentId, region,
|
|
|
157
158
|
});
|
|
158
159
|
} catch (error) {
|
|
159
160
|
if (error instanceof Error) throw error;
|
|
160
|
-
throw new Error(
|
|
161
|
+
throw new Error('Unable to list connector connections');
|
|
161
162
|
}
|
|
162
163
|
}
|
|
163
164
|
|
|
@@ -245,10 +246,9 @@ async function createConnection(connectorId, environmentId, region, token, displ
|
|
|
245
246
|
body: JSON.stringify(body)
|
|
246
247
|
});
|
|
247
248
|
if (!res.ok) {
|
|
248
|
-
const errText = await res.text().catch(() => res.statusText);
|
|
249
249
|
const result = {
|
|
250
250
|
status: 'error',
|
|
251
|
-
message: `HTTP ${res.status}
|
|
251
|
+
message: `Unable to create the connection (HTTP ${res.status})`
|
|
252
252
|
};
|
|
253
253
|
scenario.completeWithError({
|
|
254
254
|
httpStatus: res.status
|
|
@@ -281,14 +281,13 @@ async function createConnection(connectorId, environmentId, region, token, displ
|
|
|
281
281
|
body: JSON.stringify((0, _connectionMetadata.buildConsentLinkDefinition)(oauthRedirectUrl))
|
|
282
282
|
});
|
|
283
283
|
if (!consentRes.ok) {
|
|
284
|
-
const errorText = await consentRes.text().catch(() => consentRes.statusText);
|
|
285
284
|
scenario.completeWithError({
|
|
286
285
|
httpStatus: consentRes.status,
|
|
287
286
|
phase: 'getConsentLink'
|
|
288
287
|
});
|
|
289
288
|
return {
|
|
290
289
|
status: 'error',
|
|
291
|
-
message: `Unable to start connector consent (HTTP ${consentRes.status})
|
|
290
|
+
message: `Unable to start connector consent (HTTP ${consentRes.status})`
|
|
292
291
|
};
|
|
293
292
|
}
|
|
294
293
|
const consentData = await consentRes.json();
|
|
@@ -317,7 +316,7 @@ async function createConnection(connectorId, environmentId, region, token, displ
|
|
|
317
316
|
});
|
|
318
317
|
return {
|
|
319
318
|
status: 'error',
|
|
320
|
-
message:
|
|
319
|
+
message: 'Unable to create the connection.'
|
|
321
320
|
};
|
|
322
321
|
}
|
|
323
322
|
}
|
|
@@ -345,14 +344,13 @@ async function repairConnection(connectorId, connectionName, environmentId, regi
|
|
|
345
344
|
})
|
|
346
345
|
});
|
|
347
346
|
if (!updateResponse.ok) {
|
|
348
|
-
const errorText = await updateResponse.text().catch(() => updateResponse.statusText);
|
|
349
347
|
scenario.completeWithError({
|
|
350
348
|
httpStatus: updateResponse.status,
|
|
351
349
|
phase: 'update'
|
|
352
350
|
});
|
|
353
351
|
return {
|
|
354
352
|
status: 'error',
|
|
355
|
-
message: `Unable to update the connection (HTTP ${updateResponse.status})
|
|
353
|
+
message: `Unable to update the connection (HTTP ${updateResponse.status})`
|
|
356
354
|
};
|
|
357
355
|
}
|
|
358
356
|
if (!requiresApiConsent) {
|
|
@@ -371,7 +369,7 @@ async function repairConnection(connectorId, connectionName, environmentId, regi
|
|
|
371
369
|
});
|
|
372
370
|
return {
|
|
373
371
|
status: 'error',
|
|
374
|
-
message:
|
|
372
|
+
message: 'Unable to update the connection.'
|
|
375
373
|
};
|
|
376
374
|
}
|
|
377
375
|
}
|
|
@@ -396,14 +394,13 @@ async function repairConnection(connectorId, connectionName, environmentId, regi
|
|
|
396
394
|
body: JSON.stringify((0, _connectionMetadata.buildConsentLinkDefinition)(oauthRedirectUrl))
|
|
397
395
|
});
|
|
398
396
|
if (!response.ok) {
|
|
399
|
-
const errorText = await response.text().catch(() => response.statusText);
|
|
400
397
|
scenario.completeWithError({
|
|
401
398
|
httpStatus: response.status,
|
|
402
399
|
phase: 'getConsentLink'
|
|
403
400
|
});
|
|
404
401
|
return {
|
|
405
402
|
status: 'error',
|
|
406
|
-
message: `Unable to repair connector consent (HTTP ${response.status})
|
|
403
|
+
message: `Unable to repair connector consent (HTTP ${response.status})`
|
|
407
404
|
};
|
|
408
405
|
}
|
|
409
406
|
const data = await response.json();
|
|
@@ -432,7 +429,7 @@ async function repairConnection(connectorId, connectionName, environmentId, regi
|
|
|
432
429
|
});
|
|
433
430
|
return {
|
|
434
431
|
status: 'error',
|
|
435
|
-
message:
|
|
432
|
+
message: 'Unable to repair connector consent.'
|
|
436
433
|
};
|
|
437
434
|
}
|
|
438
435
|
}
|
|
@@ -602,8 +599,7 @@ async function fetchAppConnectionUsages(appId, environmentId, region, token) {
|
|
|
602
599
|
}
|
|
603
600
|
});
|
|
604
601
|
if (!response.ok) {
|
|
605
|
-
|
|
606
|
-
throw new Error(`Unable to load app connection usages (HTTP ${response.status}): ${errorText}`);
|
|
602
|
+
throw new Error(`Unable to load app connection usages (HTTP ${response.status})`);
|
|
607
603
|
}
|
|
608
604
|
const data = await response.json();
|
|
609
605
|
return (data.value ?? []).filter(usage => Boolean(usage.name && usage.properties?.reference && usage.properties?.apiId)).map(usage => ({
|
|
@@ -625,8 +621,7 @@ async function postConnectionUsage(appId, usage, environmentId, region, token) {
|
|
|
625
621
|
body: JSON.stringify((0, _connectionMetadata.buildConnectionUsageRequest)(appId, usage))
|
|
626
622
|
});
|
|
627
623
|
if (!response.ok) {
|
|
628
|
-
|
|
629
|
-
throw new Error(`Unable to register connection usage (HTTP ${response.status}): ${errorText}`);
|
|
624
|
+
throw new Error(`Unable to register connection usage (HTTP ${response.status})`);
|
|
630
625
|
}
|
|
631
626
|
}
|
|
632
627
|
async function reportScopeConsents(appId, usages, environmentId, region, token) {
|
|
@@ -641,8 +636,7 @@ async function reportScopeConsents(appId, usages, environmentId, region, token)
|
|
|
641
636
|
body: JSON.stringify((0, _connectionMetadata.buildScopeConsentRequest)(usages))
|
|
642
637
|
});
|
|
643
638
|
if (!response.ok) {
|
|
644
|
-
|
|
645
|
-
throw new Error(`Unable to register connection scope consent (HTTP ${response.status}): ${errorText}`);
|
|
639
|
+
throw new Error(`Unable to register connection scope consent (HTTP ${response.status})`);
|
|
646
640
|
}
|
|
647
641
|
}
|
|
648
642
|
|
|
@@ -667,7 +661,7 @@ async function reportScopeConsents(appId, usages, environmentId, region, token)
|
|
|
667
661
|
* @param environmentId - Environment GUID.
|
|
668
662
|
* @param region - Cluster region (default "prod").
|
|
669
663
|
* @param token - Bearer token for {@link powerPlatformScope}`(region)`.
|
|
670
|
-
* @returns Processed launch data
|
|
664
|
+
* @returns Processed launch data. Missing or inaccessible apps throw a typed error.
|
|
671
665
|
*/
|
|
672
666
|
async function fetchAppLaunchData(appId, environmentId, region, token) {
|
|
673
667
|
const base = (0, _PowerPlatformEnvironmentApi.buildEnvironmentEndpoint)(environmentId, region);
|
|
@@ -683,16 +677,17 @@ async function fetchAppLaunchData(appId, environmentId, region, token) {
|
|
|
683
677
|
body: '{}'
|
|
684
678
|
});
|
|
685
679
|
} catch (error) {
|
|
686
|
-
throw new
|
|
680
|
+
throw new _errors.TargetRuntimeError('service-unavailable', 'launch', true, 'The app launch service is temporarily unavailable. Try again.', undefined, error);
|
|
687
681
|
}
|
|
688
682
|
if (!res.ok) {
|
|
689
683
|
const responseText = await res.text().catch(() => res.statusText);
|
|
690
684
|
if ((0, _connectionMetadata.isAppLaunchAccessDenied)(res.status, responseText)) {
|
|
691
|
-
throw new
|
|
685
|
+
throw new _errors.TargetRuntimeError('app-access-denied', 'launch', false, 'You do not have access to this app. Ask the app owner or an administrator to share it with you.');
|
|
692
686
|
}
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
687
|
+
if (!(0, _connectionMetadata.getAppLaunchFailureMessage)(res.status, res.statusText, responseText)) {
|
|
688
|
+
throw new _errors.TargetRuntimeError('app-not-found', 'launch', false, 'The configured app is missing from the selected environment.');
|
|
689
|
+
}
|
|
690
|
+
throw new _errors.TargetRuntimeError('service-unavailable', 'launch', res.status === 429 || res.status >= 500, res.status === 429 || res.status >= 500 ? 'The app launch service is temporarily unavailable. Try again.' : 'The app could not be loaded from the selected environment.');
|
|
696
691
|
}
|
|
697
692
|
try {
|
|
698
693
|
const data = await res.json();
|
|
@@ -857,9 +852,11 @@ async function fetchAppLaunchData(appId, environmentId, region, token) {
|
|
|
857
852
|
}
|
|
858
853
|
}
|
|
859
854
|
const conflictingDataSources = (0, _types2.findConnectionDataSourceConflicts)(connectionRequirements);
|
|
855
|
+
const telemetryLocation = (0, _powerAppsNativeCommon.tryResolveTelemetryLocationFromEnvironment)(data.app?.properties?.environment?.properties?.location, data.app?.properties?.environment?.properties?.cluster?.geoShortName);
|
|
860
856
|
const result = {
|
|
861
857
|
connectionRefs,
|
|
862
858
|
dataverseOrgUrl,
|
|
859
|
+
telemetryLocation,
|
|
863
860
|
databaseReferences: data.app?.properties?.databaseReferences ?? {},
|
|
864
861
|
missingConnectors,
|
|
865
862
|
missingConnectionReferences,
|
|
@@ -895,7 +892,7 @@ async function fetchAppLaunchData(appId, environmentId, region, token) {
|
|
|
895
892
|
}));
|
|
896
893
|
return result;
|
|
897
894
|
} catch (error) {
|
|
898
|
-
throw new Error(
|
|
895
|
+
throw new Error('The Power Platform app launch response was invalid');
|
|
899
896
|
}
|
|
900
897
|
}
|
|
901
898
|
|
|
@@ -60,9 +60,9 @@ function hasOAuthParameter(parameters) {
|
|
|
60
60
|
function buildConnectionUserConsentUrl(environmentEndpoint, connectorId, connectionId) {
|
|
61
61
|
return `${environmentEndpoint}/connectivity/connectors/${encodeURIComponent((0, _types.getConnectorId)(connectorId))}` + `/connections/${encodeURIComponent(connectionId)}/userConsent?api-version=1`;
|
|
62
62
|
}
|
|
63
|
-
function getAppLaunchFailureMessage(status,
|
|
63
|
+
function getAppLaunchFailureMessage(status, _statusText, _responseText) {
|
|
64
64
|
if (status === 404) return null;
|
|
65
|
-
return `Unable to load the published app (HTTP ${status})
|
|
65
|
+
return `Unable to load the published app (HTTP ${status})`;
|
|
66
66
|
}
|
|
67
67
|
function isAppLaunchAccessDenied(status, responseText) {
|
|
68
68
|
if (status !== 403) return false;
|
|
@@ -100,8 +100,12 @@ async function resolveViaStaticRefs({
|
|
|
100
100
|
Authorization: `Bearer ${token}`
|
|
101
101
|
};
|
|
102
102
|
|
|
103
|
-
//
|
|
104
|
-
|
|
103
|
+
// GDS provides authoritative geography when the environment has Dataverse,
|
|
104
|
+
// even if this app does not use it. Only Dataverse apps require the lookup.
|
|
105
|
+
const [instanceMetadata, ...connResults] = await Promise.all([(0, _DataverseGlobalDiscoveryApi.fetchInstanceMetadata)(environmentId, region, acquireToken, token).catch(error => {
|
|
106
|
+
if (hasDataverse) throw error;
|
|
107
|
+
return undefined;
|
|
108
|
+
}), ...Object.entries(directConnectionReferences).map(async ([localRefId, ref]) => {
|
|
105
109
|
const apiId = (0, _types.normalizeApiId)(ref.id);
|
|
106
110
|
const schema = schemaByApiId[apiId];
|
|
107
111
|
if (!schema) {
|
|
@@ -257,7 +261,7 @@ async function resolveViaStaticRefs({
|
|
|
257
261
|
unresolvedApiIds.push((0, _types.normalizeApiId)(dr.apiId));
|
|
258
262
|
}
|
|
259
263
|
}
|
|
260
|
-
const dataverseOrgUrl = (instanceUrl ?? '').replace(/\/$/, '');
|
|
264
|
+
const dataverseOrgUrl = (instanceMetadata?.instanceUrl ?? '').replace(/\/$/, '');
|
|
261
265
|
|
|
262
266
|
// Build the final AppConnectionReference map.
|
|
263
267
|
const connectionRefs = {};
|
|
@@ -300,6 +304,7 @@ async function resolveViaStaticRefs({
|
|
|
300
304
|
return {
|
|
301
305
|
connectionRefs,
|
|
302
306
|
dataverseOrgUrl,
|
|
307
|
+
telemetryLocation: instanceMetadata?.telemetryLocation,
|
|
303
308
|
databaseReferences: {},
|
|
304
309
|
missingConnectors: [...new Set(unresolvedApiIds)],
|
|
305
310
|
missingConnectionReferences,
|
|
@@ -111,7 +111,7 @@ function hasCompleteStaticConnectionConfig(connectionReferences, schemaMap) {
|
|
|
111
111
|
const directReferences = references.filter(reference => !isCompositeConnectionApiId(reference.id));
|
|
112
112
|
return directReferences.length > 0 && directReferences.every(reference => Boolean(reference.id) && reference.dataSources.length > 0 && reference.dataSources.every(dataSource => Boolean(schemaMap[dataSource]?.name && schemaMap[dataSource]?.properties?.primaryRuntimeUrl)));
|
|
113
113
|
}
|
|
114
|
-
function buildConnectionResolutionKey(appId, environmentId, connectionReferences) {
|
|
114
|
+
function buildConnectionResolutionKey(appId, environmentId, connectionReferences, targetIdentity) {
|
|
115
115
|
const references = Object.entries(connectionReferences).sort(([left], [right]) => left.localeCompare(right)).map(([localRefId, reference]) => ({
|
|
116
116
|
localRefId,
|
|
117
117
|
id: reference.id,
|
|
@@ -122,7 +122,14 @@ function buildConnectionResolutionKey(appId, environmentId, connectionReferences
|
|
|
122
122
|
return JSON.stringify({
|
|
123
123
|
appId: appId ?? '',
|
|
124
124
|
environmentId,
|
|
125
|
-
references
|
|
125
|
+
references,
|
|
126
|
+
...(targetIdentity ? {
|
|
127
|
+
tenantId: targetIdentity.tenantId?.toLowerCase() ?? '',
|
|
128
|
+
appName: targetIdentity.appName ?? '',
|
|
129
|
+
region: targetIdentity.region?.toLowerCase() ?? '',
|
|
130
|
+
generation: targetIdentity.generation ?? 0,
|
|
131
|
+
accountIdentifier: targetIdentity.accountIdentifier ?? ''
|
|
132
|
+
} : {})
|
|
126
133
|
});
|
|
127
134
|
}
|
|
128
135
|
|
|
@@ -8,6 +8,7 @@ var _react = require("react");
|
|
|
8
8
|
var _isDevPlayer = require("../utils/isDevPlayer.js");
|
|
9
9
|
var _PowerPlatformEnvironmentApi = require("../platform/PowerPlatformEnvironmentApi.js");
|
|
10
10
|
var _PowerPlatformConnectionApi = require("./PowerPlatformConnectionApi.js");
|
|
11
|
+
var _errors = require("../target/errors.js");
|
|
11
12
|
var _resolveConnections = require("./resolveConnections.js");
|
|
12
13
|
var _connectionState = require("./connectionState.js");
|
|
13
14
|
var _logger = require("../logger.js");
|
|
@@ -71,7 +72,9 @@ function useConnectionRefs({
|
|
|
71
72
|
schemaMap,
|
|
72
73
|
onDisambiguationNeeded,
|
|
73
74
|
onMissingConnection,
|
|
74
|
-
isvClientId
|
|
75
|
+
isvClientId,
|
|
76
|
+
onTelemetryLocationResolved,
|
|
77
|
+
targetIdentity
|
|
75
78
|
}) {
|
|
76
79
|
const [connectionRefs, setConnectionRefs] = (0, _react.useState)({});
|
|
77
80
|
const [isReady, setIsReady] = (0, _react.useState)(false);
|
|
@@ -94,7 +97,10 @@ function useConnectionRefs({
|
|
|
94
97
|
const isBackgroundRetryRef = (0, _react.useRef)(false);
|
|
95
98
|
const MAX_TOKEN_RETRIES = 5;
|
|
96
99
|
const TOKEN_RETRY_DELAY_MS = 2000;
|
|
97
|
-
const currentConfigKey = (0, _types.buildConnectionResolutionKey)(appId, environmentId, _isDevPlayer.isDevPlayer ? connectionReferences : {}
|
|
100
|
+
const currentConfigKey = (0, _types.buildConnectionResolutionKey)(appId, environmentId, _isDevPlayer.isDevPlayer ? connectionReferences : {}, targetIdentity ? {
|
|
101
|
+
...targetIdentity,
|
|
102
|
+
region
|
|
103
|
+
} : undefined);
|
|
98
104
|
(0, _react.useEffect)(() => {
|
|
99
105
|
if (!isSignedIn) {
|
|
100
106
|
tokenRetryCountRef.current = 0;
|
|
@@ -203,9 +209,6 @@ function useConnectionRefs({
|
|
|
203
209
|
let result = null;
|
|
204
210
|
if (appId && environmentId && !_isDevPlayer.isDevPlayer) {
|
|
205
211
|
result = await (0, _PowerPlatformConnectionApi.fetchAppLaunchData)(appId, environmentId, region, token);
|
|
206
|
-
if (!result) {
|
|
207
|
-
throw new Error(`App "${appId}" is not available in environment "${environmentId}". Push the mobile app to this environment and try again.`);
|
|
208
|
-
}
|
|
209
212
|
if (result && onDisambiguationNeeded) {
|
|
210
213
|
for (const requirement of [...result.missingConnectionReferences]) {
|
|
211
214
|
const candidates = result.connectionCandidates[requirement.localRefId] ?? [];
|
|
@@ -263,6 +266,9 @@ function useConnectionRefs({
|
|
|
263
266
|
});
|
|
264
267
|
return;
|
|
265
268
|
}
|
|
269
|
+
if (result.telemetryLocation) {
|
|
270
|
+
onTelemetryLocationResolved?.(result.telemetryLocation);
|
|
271
|
+
}
|
|
266
272
|
|
|
267
273
|
// Pre-warm the Dataverse token while the user waits.
|
|
268
274
|
if (result.dataverseOrgUrl) {
|
|
@@ -299,15 +305,18 @@ function useConnectionRefs({
|
|
|
299
305
|
errorName: e instanceof Error ? e.name : 'UnknownError'
|
|
300
306
|
});
|
|
301
307
|
(0, _index.getAppLogger)().trackErrorEvent(_index.HostOperationNames.PlatformLoadConnectionsFailed, {
|
|
302
|
-
|
|
308
|
+
errorName: e instanceof Error ? e.name : 'UnknownError',
|
|
309
|
+
errorCode: e instanceof _errors.TargetRuntimeError ? e.code : undefined,
|
|
303
310
|
hasAppId: Boolean(appId),
|
|
304
311
|
hasEnvironmentId: Boolean(environmentId),
|
|
305
312
|
connectionReferenceCount: Object.keys(connectionReferences).length,
|
|
306
313
|
runningInDevPlayer: _isDevPlayer.isDevPlayer
|
|
307
314
|
});
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
315
|
+
const targetError = e instanceof _errors.TargetRuntimeError ? e : null;
|
|
316
|
+
const accessDenied = targetError?.code === 'app-access-denied';
|
|
317
|
+
const appNotFound = targetError?.code === 'app-not-found';
|
|
318
|
+
setErrorKind(accessDenied ? 'app-access-denied' : appNotFound ? 'app-not-found' : 'generic');
|
|
319
|
+
setError(targetError?.userMessage ?? (accessDenied ? 'You don’t have access to this app. Ask the app owner or an administrator to share it with you, then try again.' : 'Unable to load this app from Power Platform. Try again later.'));
|
|
311
320
|
setIsReady(true);
|
|
312
321
|
}
|
|
313
322
|
});
|
|
@@ -316,7 +325,7 @@ function useConnectionRefs({
|
|
|
316
325
|
if (retryTimer) clearTimeout(retryTimer);
|
|
317
326
|
};
|
|
318
327
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
319
|
-
}, [isSignedIn, retryTick, appId, environmentId, region, connectionReferences, hasDataverse, schemaMap, isvClientId]);
|
|
328
|
+
}, [isSignedIn, retryTick, appId, environmentId, region, connectionReferences, hasDataverse, schemaMap, isvClientId, onTelemetryLocationResolved, targetIdentity?.tenantId, targetIdentity?.appName, targetIdentity?.generation, targetIdentity?.accountIdentifier]);
|
|
320
329
|
return {
|
|
321
330
|
connectionRefs,
|
|
322
331
|
isReady,
|
package/lib/commonjs/index.js
CHANGED
|
@@ -63,6 +63,12 @@ Object.defineProperty(exports, "PowerAppsProvider", {
|
|
|
63
63
|
return _PowerAppsProvider.PowerAppsProvider;
|
|
64
64
|
}
|
|
65
65
|
});
|
|
66
|
+
Object.defineProperty(exports, "PowerAppsRuntimeGate", {
|
|
67
|
+
enumerable: true,
|
|
68
|
+
get: function () {
|
|
69
|
+
return _PowerAppsExperience.PowerAppsRuntimeGate;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
66
72
|
Object.defineProperty(exports, "PowerPagesBridge", {
|
|
67
73
|
enumerable: true,
|
|
68
74
|
get: function () {
|
|
@@ -87,6 +93,12 @@ Object.defineProperty(exports, "base64ToUint8Array", {
|
|
|
87
93
|
return _dataverseUtils.base64ToUint8Array;
|
|
88
94
|
}
|
|
89
95
|
});
|
|
96
|
+
Object.defineProperty(exports, "buildCanvasAppResolutionUrl", {
|
|
97
|
+
enumerable: true,
|
|
98
|
+
get: function () {
|
|
99
|
+
return _CanvasAppResolution.buildCanvasAppResolutionUrl;
|
|
100
|
+
}
|
|
101
|
+
});
|
|
90
102
|
Object.defineProperty(exports, "buildCdsDataSources", {
|
|
91
103
|
enumerable: true,
|
|
92
104
|
get: function () {
|
|
@@ -159,6 +171,12 @@ Object.defineProperty(exports, "buildScopeConsentUrl", {
|
|
|
159
171
|
return _connectionMetadata.buildScopeConsentUrl;
|
|
160
172
|
}
|
|
161
173
|
});
|
|
174
|
+
Object.defineProperty(exports, "canonicalizeDataverseOrigin", {
|
|
175
|
+
enumerable: true,
|
|
176
|
+
get: function () {
|
|
177
|
+
return _CanvasAppResolution.canonicalizeDataverseOrigin;
|
|
178
|
+
}
|
|
179
|
+
});
|
|
162
180
|
Object.defineProperty(exports, "completePowerAppsAuthSession", {
|
|
163
181
|
enumerable: true,
|
|
164
182
|
get: function () {
|
|
@@ -279,6 +297,12 @@ Object.defineProperty(exports, "hasOAuthParameter", {
|
|
|
279
297
|
return _connectionMetadata.hasOAuthParameter;
|
|
280
298
|
}
|
|
281
299
|
});
|
|
300
|
+
Object.defineProperty(exports, "invalidateNativeHostRuntime", {
|
|
301
|
+
enumerable: true,
|
|
302
|
+
get: function () {
|
|
303
|
+
return _setupNativeHost.invalidateNativeHostRuntime;
|
|
304
|
+
}
|
|
305
|
+
});
|
|
282
306
|
Object.defineProperty(exports, "isCompositeConnectionApiId", {
|
|
283
307
|
enumerable: true,
|
|
284
308
|
get: function () {
|
|
@@ -333,6 +357,12 @@ Object.defineProperty(exports, "reportScopeConsents", {
|
|
|
333
357
|
return _PowerPlatformConnectionApi.reportScopeConsents;
|
|
334
358
|
}
|
|
335
359
|
});
|
|
360
|
+
Object.defineProperty(exports, "resolveCanvasApp", {
|
|
361
|
+
enumerable: true,
|
|
362
|
+
get: function () {
|
|
363
|
+
return _CanvasAppResolutionApi.resolveCanvasApp;
|
|
364
|
+
}
|
|
365
|
+
});
|
|
336
366
|
Object.defineProperty(exports, "serializeConnectionParameters", {
|
|
337
367
|
enumerable: true,
|
|
338
368
|
get: function () {
|
|
@@ -387,6 +417,12 @@ Object.defineProperty(exports, "useConnectionSetup", {
|
|
|
387
417
|
return _useConnectionSetup.useConnectionSetup;
|
|
388
418
|
}
|
|
389
419
|
});
|
|
420
|
+
Object.defineProperty(exports, "usePowerAppsExperience", {
|
|
421
|
+
enumerable: true,
|
|
422
|
+
get: function () {
|
|
423
|
+
return _PowerAppsExperience.usePowerAppsExperience;
|
|
424
|
+
}
|
|
425
|
+
});
|
|
390
426
|
Object.defineProperty(exports, "useThemeControl", {
|
|
391
427
|
enumerable: true,
|
|
392
428
|
get: function () {
|
|
@@ -408,8 +444,11 @@ var _AuthContext = require("./auth/AuthContext");
|
|
|
408
444
|
var _completePowerAppsAuthSession = require("./auth/completePowerAppsAuthSession.js");
|
|
409
445
|
var _PowerAppsHostProvider = require("./provider/PowerAppsHostProvider");
|
|
410
446
|
var _PowerAppsProvider = require("./provider/PowerAppsProvider.js");
|
|
447
|
+
var _PowerAppsExperience = require("./target/PowerAppsExperience.js");
|
|
411
448
|
var _PowerPlatformEnvironmentApi = require("./platform/PowerPlatformEnvironmentApi.js");
|
|
412
449
|
var _DataverseGlobalDiscoveryApi = require("./platform/DataverseGlobalDiscoveryApi.js");
|
|
450
|
+
var _CanvasAppResolution = require("./platform/CanvasAppResolution.js");
|
|
451
|
+
var _CanvasAppResolutionApi = require("./platform/CanvasAppResolutionApi.js");
|
|
413
452
|
var _DataverseDataSources = require("./platform/DataverseDataSources.js");
|
|
414
453
|
var _useConnectionRefs = require("./connections/useConnectionRefs.js");
|
|
415
454
|
var _PowerPlatformConnectionApi = require("./connections/PowerPlatformConnectionApi.js");
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.areDataverseOriginAliases = areDataverseOriginAliases;
|
|
7
|
+
exports.buildCanvasAppResolutionUrl = buildCanvasAppResolutionUrl;
|
|
8
|
+
exports.canonicalizeDataverseOrigin = canonicalizeDataverseOrigin;
|
|
9
|
+
function canonicalizeDataverseOrigin(value) {
|
|
10
|
+
const url = new URL(value);
|
|
11
|
+
if (url.protocol !== 'https:' || url.username || url.password || url.search || url.hash || url.pathname !== '' && url.pathname !== '/') {
|
|
12
|
+
throw new Error('Dataverse URL must be an HTTPS origin');
|
|
13
|
+
}
|
|
14
|
+
return url.origin.toLowerCase();
|
|
15
|
+
}
|
|
16
|
+
function areDataverseOriginAliases(left, right) {
|
|
17
|
+
const normalizeHost = value => {
|
|
18
|
+
const url = new URL(canonicalizeDataverseOrigin(value));
|
|
19
|
+
const labels = url.hostname.split('.');
|
|
20
|
+
if (labels[1] === 'api') labels.splice(1, 1);
|
|
21
|
+
return `${labels.join('.')}:${url.port}`;
|
|
22
|
+
};
|
|
23
|
+
return normalizeHost(left) === normalizeHost(right);
|
|
24
|
+
}
|
|
25
|
+
function buildCanvasAppResolutionUrl(dataverseApiUrl, appName) {
|
|
26
|
+
const origin = canonicalizeDataverseOrigin(dataverseApiUrl);
|
|
27
|
+
const query = new URLSearchParams({
|
|
28
|
+
'$select': 'name,uniquecanvasappid,canvasapptype,componentstate',
|
|
29
|
+
'$filter': 'name eq @appName and componentstate eq 0',
|
|
30
|
+
'@appName': `'${appName.replace(/'/g, "''")}'`
|
|
31
|
+
});
|
|
32
|
+
return `${origin}/api/data/v9.2/canvasapps?${query.toString()}`;
|
|
33
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.resolveCanvasApp = resolveCanvasApp;
|
|
7
|
+
var _http = require("@microsoft/power-apps-native-common/http");
|
|
8
|
+
var _index = require("../telemetry/index.js");
|
|
9
|
+
var _errors = require("../target/errors.js");
|
|
10
|
+
var _CanvasAppResolution = require("./CanvasAppResolution.js");
|
|
11
|
+
const GUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
12
|
+
async function resolveCanvasApp(target, dataverseApiUrl, acquireToken, generation) {
|
|
13
|
+
const assertCurrent = () => {
|
|
14
|
+
if (generation && !generation.isCurrent(generation.value)) {
|
|
15
|
+
throw new _errors.TargetRuntimeError('stale-operation', 'app-resolution', false, 'The selected environment changed before app resolution completed.', target.id);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const origin = (0, _CanvasAppResolution.canonicalizeDataverseOrigin)(dataverseApiUrl);
|
|
19
|
+
const scenario = (0, _index.getAppLogger)().trackScenario(_index.HostOperationNames.PlatformFetchInstanceUrl, {
|
|
20
|
+
targetId: target.id,
|
|
21
|
+
generation: generation?.value
|
|
22
|
+
});
|
|
23
|
+
try {
|
|
24
|
+
const token = await acquireToken([`${origin}/user_impersonation`]);
|
|
25
|
+
assertCurrent();
|
|
26
|
+
if (!token) {
|
|
27
|
+
throw new _errors.TargetRuntimeError('app-metadata-access-denied', 'app-resolution', false, 'You do not have access to app metadata in the selected environment.', target.id);
|
|
28
|
+
}
|
|
29
|
+
const response = await _index.platformHttpClient.request((0, _CanvasAppResolution.buildCanvasAppResolutionUrl)(origin, target.appName), {
|
|
30
|
+
headers: {
|
|
31
|
+
Authorization: `Bearer ${token}`,
|
|
32
|
+
Accept: 'application/json'
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
assertCurrent();
|
|
36
|
+
if (response.status === 401 || response.status === 403) {
|
|
37
|
+
throw new _errors.TargetRuntimeError('app-metadata-access-denied', 'app-resolution', false, 'You do not have access to app metadata in the selected environment.', target.id);
|
|
38
|
+
}
|
|
39
|
+
if (!response.ok) throw new Error(`Canvas App metadata request failed with status ${response.status}`);
|
|
40
|
+
const payload = await response.json();
|
|
41
|
+
assertCurrent();
|
|
42
|
+
const rows = payload.value ?? [];
|
|
43
|
+
if (rows.length === 0) {
|
|
44
|
+
throw new _errors.TargetRuntimeError('app-metadata-missing', 'app-resolution', false, 'The configured app is missing from the selected environment.', target.id);
|
|
45
|
+
}
|
|
46
|
+
if (rows.length !== 1) {
|
|
47
|
+
throw new _errors.TargetRuntimeError('app-metadata-ambiguous', 'app-resolution', false, 'More than one app matches the selected environment configuration.', target.id);
|
|
48
|
+
}
|
|
49
|
+
const row = rows[0];
|
|
50
|
+
const appId = row.uniquecanvasappid?.toLowerCase();
|
|
51
|
+
if (row.name !== target.appName || row.componentstate !== 0 || !appId || !GUID_PATTERN.test(appId)) {
|
|
52
|
+
throw new _errors.TargetRuntimeError('app-metadata-missing', 'app-resolution', false, 'The configured app metadata is invalid in the selected environment.', target.id);
|
|
53
|
+
}
|
|
54
|
+
scenario.complete({
|
|
55
|
+
...(0, _http.getHttpCorrelationProperties)(response.headers)
|
|
56
|
+
});
|
|
57
|
+
return {
|
|
58
|
+
...target,
|
|
59
|
+
appId,
|
|
60
|
+
dataverseApiUrl: origin
|
|
61
|
+
};
|
|
62
|
+
} catch (error) {
|
|
63
|
+
scenario.fail(undefined, {
|
|
64
|
+
errorCode: error instanceof _errors.TargetRuntimeError ? error.code : 'service-unavailable'
|
|
65
|
+
});
|
|
66
|
+
if (error instanceof _errors.TargetRuntimeError) throw error;
|
|
67
|
+
throw new _errors.TargetRuntimeError('service-unavailable', 'app-resolution', true, 'App metadata is temporarily unavailable. Try again.', target.id, error);
|
|
68
|
+
}
|
|
69
|
+
}
|