@microsoft/power-apps-native-host 0.2.25 → 0.3.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 +21 -2
- package/compatibility/current.json +0 -1
- package/lib/NOTICE.txt +6 -12
- package/lib/commonjs/auth/AuthContext.js +74 -18
- package/lib/commonjs/bridge/NativePowerAppsBridge.js +11 -18
- package/lib/commonjs/bridge/PowerPagesBridge.js +27 -8
- package/lib/commonjs/bridge/bridgeState.js +40 -0
- package/lib/commonjs/connections/ConnectionSetupScreen.js +459 -80
- package/lib/commonjs/connections/PowerPlatformConnectionApi.js +946 -0
- package/lib/commonjs/connections/connectionMetadata.js +127 -0
- package/lib/commonjs/connections/connectionState.js +96 -0
- package/lib/commonjs/connections/disambiguate.js +19 -11
- package/lib/commonjs/connections/resolveConnections.js +347 -0
- package/lib/commonjs/connections/types.js +106 -0
- package/lib/commonjs/{platform → connections}/useConnectionRefs.js +128 -65
- package/lib/commonjs/connections/useConnectionSetup.js +111 -52
- package/lib/commonjs/controls/FilePicker.js +6 -0
- package/lib/commonjs/controls/ImagePicker.js +8 -0
- package/lib/commonjs/index.js +166 -4
- package/lib/commonjs/logger.js +9 -6
- package/lib/commonjs/offline/createOfflineHostActivationCallbacks.js +61 -0
- package/lib/commonjs/offline/useOfflineIntegrationWithStartupGate.js +46 -0
- package/lib/commonjs/offline/useOfflineIntegrationWithStartupGate.web.js +13 -0
- package/lib/commonjs/platform/DataverseDataSources.js +14 -0
- package/lib/commonjs/platform/DataverseGlobalDiscoveryApi.js +49 -0
- package/lib/commonjs/platform/GlobalDiscovery.js +51 -0
- package/lib/commonjs/platform/PowerPlatformEnvironmentApi.js +6 -106
- package/lib/commonjs/provider/PowerAppsHostProvider.js +156 -25
- package/lib/commonjs/provider/PowerAppsProvider.js +173 -134
- package/lib/commonjs/provider/SessionDetailsGesture.js +216 -0
- package/lib/commonjs/telemetry/OfflineSdkTelemetry.js +95 -0
- package/lib/commonjs/telemetry/OfflineSdkTelemetry.web.js +9 -0
- package/lib/commonjs/telemetry/index.js +22 -3
- package/lib/module/auth/AuthContext.js +74 -18
- package/lib/module/bridge/NativePowerAppsBridge.js +12 -19
- package/lib/module/bridge/PowerPagesBridge.js +27 -8
- package/lib/module/bridge/bridgeState.js +34 -0
- package/lib/module/connections/ConnectionSetupScreen.js +460 -81
- package/lib/module/connections/PowerPlatformConnectionApi.js +932 -0
- package/lib/module/connections/connectionMetadata.js +112 -0
- package/lib/module/connections/connectionState.js +82 -0
- package/lib/module/connections/disambiguate.js +19 -12
- package/lib/module/connections/resolveConnections.js +342 -0
- package/lib/module/connections/types.js +91 -0
- package/lib/module/{platform → connections}/useConnectionRefs.js +129 -66
- package/lib/module/connections/useConnectionSetup.js +113 -54
- package/lib/module/controls/FilePicker.js +6 -0
- package/lib/module/controls/ImagePicker.js +8 -0
- package/lib/module/index.js +10 -4
- package/lib/module/logger.js +8 -6
- package/lib/module/offline/createOfflineHostActivationCallbacks.js +55 -0
- package/lib/module/offline/useOfflineIntegrationWithStartupGate.js +42 -0
- package/lib/module/offline/useOfflineIntegrationWithStartupGate.web.js +9 -0
- package/lib/module/platform/DataverseDataSources.js +10 -0
- package/lib/module/platform/DataverseGlobalDiscoveryApi.js +45 -0
- package/lib/module/platform/GlobalDiscovery.js +45 -0
- package/lib/module/platform/PowerPlatformEnvironmentApi.js +5 -104
- package/lib/module/provider/PowerAppsHostProvider.js +159 -28
- package/lib/module/provider/PowerAppsProvider.js +177 -138
- package/lib/module/provider/SessionDetailsGesture.js +211 -0
- package/lib/module/telemetry/OfflineSdkTelemetry.js +91 -0
- package/lib/module/telemetry/OfflineSdkTelemetry.web.js +5 -0
- package/lib/module/telemetry/index.js +15 -4
- package/lib/typescript/commonjs/auth/AuthContext.d.ts +3 -1
- package/lib/typescript/commonjs/auth/AuthContext.web.d.ts +1 -0
- package/lib/typescript/commonjs/bridge/bridgeState.d.ts +5 -0
- package/lib/typescript/commonjs/connections/ConnectionSetupScreen.d.ts +28 -7
- package/lib/typescript/commonjs/{platform → connections}/PowerPlatformConnectionApi.d.ts +52 -43
- package/lib/typescript/commonjs/connections/connectionMetadata.d.ts +66 -0
- package/lib/typescript/commonjs/connections/connectionState.d.ts +16 -0
- package/lib/typescript/commonjs/connections/disambiguate.d.ts +4 -2
- package/lib/typescript/commonjs/{platform → connections}/resolveConnections.d.ts +19 -19
- package/lib/typescript/commonjs/connections/types.d.ts +129 -1
- package/lib/typescript/commonjs/{platform → connections}/useConnectionRefs.d.ts +33 -24
- package/lib/typescript/commonjs/connections/useConnectionSetup.d.ts +12 -8
- package/lib/typescript/commonjs/controls/FilePicker.d.ts +7 -1
- package/lib/typescript/commonjs/controls/ImagePicker.d.ts +9 -1
- package/lib/typescript/commonjs/index.d.ts +17 -10
- package/lib/typescript/commonjs/logger.d.ts +4 -4
- package/lib/typescript/commonjs/offline/createOfflineHostActivationCallbacks.d.ts +12 -0
- package/lib/typescript/commonjs/offline/useOfflineIntegrationWithStartupGate.d.ts +15 -0
- package/lib/typescript/commonjs/offline/useOfflineIntegrationWithStartupGate.web.d.ts +6 -0
- package/lib/typescript/commonjs/platform/DataverseDataSources.d.ts +27 -0
- package/lib/typescript/commonjs/platform/DataverseGlobalDiscoveryApi.d.ts +3 -0
- package/lib/typescript/commonjs/platform/GlobalDiscovery.d.ts +4 -0
- package/lib/typescript/commonjs/platform/PowerPlatformEnvironmentApi.d.ts +3 -74
- package/lib/typescript/commonjs/provider/PowerAppsHostProvider.d.ts +29 -18
- package/lib/typescript/commonjs/provider/PowerAppsProvider.d.ts +30 -8
- package/lib/typescript/commonjs/provider/SessionDetailsGesture.d.ts +13 -0
- package/lib/typescript/commonjs/telemetry/OfflineSdkTelemetry.d.ts +15 -0
- package/lib/typescript/commonjs/telemetry/OfflineSdkTelemetry.web.d.ts +2 -0
- package/lib/typescript/commonjs/telemetry/index.d.ts +13 -1
- package/lib/typescript/module/auth/AuthContext.d.ts +3 -1
- package/lib/typescript/module/auth/AuthContext.web.d.ts +1 -0
- package/lib/typescript/module/bridge/bridgeState.d.ts +5 -0
- package/lib/typescript/module/connections/ConnectionSetupScreen.d.ts +28 -7
- package/lib/typescript/module/{platform → connections}/PowerPlatformConnectionApi.d.ts +52 -43
- package/lib/typescript/module/connections/connectionMetadata.d.ts +66 -0
- package/lib/typescript/module/connections/connectionState.d.ts +16 -0
- package/lib/typescript/module/connections/disambiguate.d.ts +4 -2
- package/lib/typescript/module/{platform → connections}/resolveConnections.d.ts +19 -19
- package/lib/typescript/module/connections/types.d.ts +129 -1
- package/lib/typescript/module/{platform → connections}/useConnectionRefs.d.ts +33 -24
- package/lib/typescript/module/connections/useConnectionSetup.d.ts +12 -8
- package/lib/typescript/module/controls/FilePicker.d.ts +7 -1
- package/lib/typescript/module/controls/ImagePicker.d.ts +9 -1
- package/lib/typescript/module/index.d.ts +17 -10
- package/lib/typescript/module/logger.d.ts +4 -4
- package/lib/typescript/module/offline/createOfflineHostActivationCallbacks.d.ts +12 -0
- package/lib/typescript/module/offline/useOfflineIntegrationWithStartupGate.d.ts +15 -0
- package/lib/typescript/module/offline/useOfflineIntegrationWithStartupGate.web.d.ts +6 -0
- package/lib/typescript/module/platform/DataverseDataSources.d.ts +27 -0
- package/lib/typescript/module/platform/DataverseGlobalDiscoveryApi.d.ts +3 -0
- package/lib/typescript/module/platform/GlobalDiscovery.d.ts +4 -0
- package/lib/typescript/module/platform/PowerPlatformEnvironmentApi.d.ts +3 -74
- package/lib/typescript/module/provider/PowerAppsHostProvider.d.ts +29 -18
- package/lib/typescript/module/provider/PowerAppsProvider.d.ts +30 -8
- package/lib/typescript/module/provider/SessionDetailsGesture.d.ts +13 -0
- package/lib/typescript/module/telemetry/OfflineSdkTelemetry.d.ts +15 -0
- package/lib/typescript/module/telemetry/OfflineSdkTelemetry.web.d.ts +2 -0
- package/lib/typescript/module/telemetry/index.d.ts +13 -1
- package/package.json +11 -6
- package/scripts/metro-logger.js +213 -0
- package/scripts/upgrade/runtime.js +13 -7
- package/scripts/upgrade-authoring.js +5 -6
- package/lib/commonjs/platform/PowerPlatformConnectionApi.js +0 -606
- package/lib/commonjs/platform/resolveConnections.js +0 -264
- package/lib/module/platform/PowerPlatformConnectionApi.js +0 -595
- package/lib/module/platform/resolveConnections.js +0 -259
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.createHostQueryClient = createHostQueryClient;
|
|
7
|
+
exports.createOfflineHostActivationCallbacks = createOfflineHostActivationCallbacks;
|
|
8
|
+
exports.resolveSdkMountDecision = resolveSdkMountDecision;
|
|
9
|
+
var _reactQuery = require("@tanstack/react-query");
|
|
10
|
+
function setNetworkMode(queryClient, networkMode) {
|
|
11
|
+
const current = queryClient.getDefaultOptions();
|
|
12
|
+
queryClient.setDefaultOptions({
|
|
13
|
+
...current,
|
|
14
|
+
queries: {
|
|
15
|
+
...current.queries,
|
|
16
|
+
staleTime: 60_000,
|
|
17
|
+
retry: 2,
|
|
18
|
+
networkMode
|
|
19
|
+
},
|
|
20
|
+
mutations: {
|
|
21
|
+
...current.mutations,
|
|
22
|
+
retry: 0,
|
|
23
|
+
networkMode
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function createHostQueryClient() {
|
|
28
|
+
return new _reactQuery.QueryClient({
|
|
29
|
+
defaultOptions: {
|
|
30
|
+
queries: {
|
|
31
|
+
staleTime: 60_000,
|
|
32
|
+
retry: 2
|
|
33
|
+
},
|
|
34
|
+
mutations: {
|
|
35
|
+
retry: 0
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function createOfflineHostActivationCallbacks(queryClient) {
|
|
41
|
+
return {
|
|
42
|
+
prepareForOfflineActivation: () => queryClient.cancelQueries(),
|
|
43
|
+
completeOfflineActivation: async () => {
|
|
44
|
+
setNetworkMode(queryClient, 'always');
|
|
45
|
+
await queryClient.invalidateQueries();
|
|
46
|
+
},
|
|
47
|
+
rollbackOfflineActivation: async () => {
|
|
48
|
+
setNetworkMode(queryClient, undefined);
|
|
49
|
+
await queryClient.invalidateQueries();
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function resolveSdkMountDecision(value) {
|
|
54
|
+
return typeof value === 'boolean' ? {
|
|
55
|
+
shouldMountChildren: value,
|
|
56
|
+
contractSupported: true
|
|
57
|
+
} : {
|
|
58
|
+
shouldMountChildren: true,
|
|
59
|
+
contractSupported: false
|
|
60
|
+
};
|
|
61
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.useOfflineIntegrationWithStartupGate = useOfflineIntegrationWithStartupGate;
|
|
7
|
+
var _react = require("react");
|
|
8
|
+
var _reactQuery = require("@tanstack/react-query");
|
|
9
|
+
var _powerAppsNativeCommon = require("@microsoft/power-apps-native-common");
|
|
10
|
+
var _DataverseGlobalDiscoveryApi = require("../platform/DataverseGlobalDiscoveryApi.js");
|
|
11
|
+
var _index = require("../telemetry/index.js");
|
|
12
|
+
var _createOfflineHostActivationCallbacks = require("./createOfflineHostActivationCallbacks.js");
|
|
13
|
+
var _useOfflineIntegration = require("./useOfflineIntegration");
|
|
14
|
+
function useOfflineIntegrationWithStartupGate({
|
|
15
|
+
powerConfig,
|
|
16
|
+
offlineProfile,
|
|
17
|
+
acquireToken,
|
|
18
|
+
hasRealAccount,
|
|
19
|
+
isAuthReady
|
|
20
|
+
}) {
|
|
21
|
+
const queryClient = (0, _reactQuery.useQueryClient)();
|
|
22
|
+
const resolveInstanceUrl = (0, _react.useCallback)(async (environmentId, region, acqToken) => (await (0, _DataverseGlobalDiscoveryApi.fetchInstanceUrl)(environmentId, region, acqToken)) ?? null, []);
|
|
23
|
+
const hostActivationCallbacks = (0, _react.useMemo)(() => (0, _createOfflineHostActivationCallbacks.createOfflineHostActivationCallbacks)(queryClient), [queryClient]);
|
|
24
|
+
const offline = (0, _useOfflineIntegration.useOfflineIntegration)({
|
|
25
|
+
powerConfig,
|
|
26
|
+
offlineProfile,
|
|
27
|
+
acquireToken,
|
|
28
|
+
isSignedIn: hasRealAccount,
|
|
29
|
+
authState: isAuthReady ? hasRealAccount ? 'authenticated' : 'signed-out' : 'hydrating',
|
|
30
|
+
fetchInstanceUrl: resolveInstanceUrl,
|
|
31
|
+
hostActivationCallbacks
|
|
32
|
+
});
|
|
33
|
+
const mountDecision = (0, _createOfflineHostActivationCallbacks.resolveSdkMountDecision)(offline.shouldMountChildren);
|
|
34
|
+
(0, _react.useEffect)(() => {
|
|
35
|
+
if (!mountDecision.contractSupported) {
|
|
36
|
+
(0, _powerAppsNativeCommon.getAppLogger)().trackErrorEvent(_index.HostOperationNames.OfflineRoutingContractUnsupported, {
|
|
37
|
+
fallbackRoute: 'online'
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}, [mountDecision.contractSupported]);
|
|
41
|
+
return {
|
|
42
|
+
isEnabled: offline.isEnabled,
|
|
43
|
+
StatusOverlay: offline.StatusOverlay,
|
|
44
|
+
shouldMountChildren: mountDecision.shouldMountChildren
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.useOfflineIntegrationWithStartupGate = useOfflineIntegrationWithStartupGate;
|
|
7
|
+
function useOfflineIntegrationWithStartupGate(_options) {
|
|
8
|
+
return {
|
|
9
|
+
isEnabled: false,
|
|
10
|
+
StatusOverlay: null,
|
|
11
|
+
shouldMountChildren: true
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.buildCdsDataSources = buildCdsDataSources;
|
|
7
|
+
function buildCdsDataSources(databaseReferences, dataverseOrgUrl) {
|
|
8
|
+
return Object.values(databaseReferences).flatMap(databaseReference => Object.values(databaseReference.dataSources ?? {}).map(dataSource => ({
|
|
9
|
+
runtimeUrl: dataverseOrgUrl ? `${dataverseOrgUrl}/api/data/v9.0/` : '',
|
|
10
|
+
entitySetName: dataSource.entitySetName ?? '',
|
|
11
|
+
logicalName: dataSource.logicalName ?? '',
|
|
12
|
+
version: '9.2'
|
|
13
|
+
})));
|
|
14
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.fetchInstanceUrl = fetchInstanceUrl;
|
|
7
|
+
var _http = require("@microsoft/power-apps-native-common/http");
|
|
8
|
+
var _index = require("../telemetry/index.js");
|
|
9
|
+
var _GlobalDiscovery = require("./GlobalDiscovery.js");
|
|
10
|
+
function getSafeErrorName(error) {
|
|
11
|
+
return error instanceof Error ? error.constructor.name : typeof error;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Fetch the Dataverse instance URL from the Dataverse Global Discovery Service. */
|
|
15
|
+
async function fetchInstanceUrl(environmentId, region, acquireToken, _bearerToken) {
|
|
16
|
+
const scenario = (0, _index.getAppLogger)().trackScenario(_index.HostOperationNames.PlatformFetchInstanceUrl, {
|
|
17
|
+
environmentId,
|
|
18
|
+
region
|
|
19
|
+
});
|
|
20
|
+
try {
|
|
21
|
+
const url = (0, _GlobalDiscovery.buildGlobalDiscoveryUrl)(environmentId, region);
|
|
22
|
+
const token = await acquireToken([(0, _GlobalDiscovery.globalDiscoveryScope)(region)]);
|
|
23
|
+
if (!token) {
|
|
24
|
+
throw new Error('fetchInstanceUrl: failed to acquire Dataverse Global Discovery token');
|
|
25
|
+
}
|
|
26
|
+
const response = await _index.platformHttpClient.request(url, {
|
|
27
|
+
method: 'GET',
|
|
28
|
+
headers: {
|
|
29
|
+
Authorization: `Bearer ${token}`,
|
|
30
|
+
'Content-Type': 'application/json'
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
throw new Error(`fetchInstanceUrl: Global Discovery returned ${response.status} ${response.statusText}`);
|
|
35
|
+
}
|
|
36
|
+
const result = await response.json();
|
|
37
|
+
const instanceUrl = result.value?.find(instance => instance.EnvironmentId?.toLowerCase() === environmentId.toLowerCase())?.ApiUrl;
|
|
38
|
+
scenario.complete({
|
|
39
|
+
hasInstanceUrl: instanceUrl !== undefined,
|
|
40
|
+
...(0, _http.getHttpCorrelationProperties)(response.headers)
|
|
41
|
+
});
|
|
42
|
+
return instanceUrl;
|
|
43
|
+
} catch (error) {
|
|
44
|
+
scenario.fail(undefined, {
|
|
45
|
+
errorName: getSafeErrorName(error)
|
|
46
|
+
});
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.buildGlobalDiscoveryUrl = buildGlobalDiscoveryUrl;
|
|
7
|
+
exports.globalDiscoveryEndpoint = globalDiscoveryEndpoint;
|
|
8
|
+
exports.globalDiscoveryScope = globalDiscoveryScope;
|
|
9
|
+
const GLOBAL_DISCOVERY_HOST = {
|
|
10
|
+
prod: 'globaldisco.crm.dynamics.com',
|
|
11
|
+
gcc: 'globaldisco.crm9.dynamics.com',
|
|
12
|
+
usg: 'globaldisco.crm.microsoftdynamics.us',
|
|
13
|
+
dod: 'globaldisco.crm.appsplatform.us',
|
|
14
|
+
china: 'globaldisco.crm.dynamics.cn'
|
|
15
|
+
};
|
|
16
|
+
const REGION_DISCOVERY_CLOUD = {
|
|
17
|
+
dev: 'prod',
|
|
18
|
+
test: 'prod',
|
|
19
|
+
preprod: 'prod',
|
|
20
|
+
preview: 'prod',
|
|
21
|
+
firstrelease: 'prod',
|
|
22
|
+
prod: 'prod',
|
|
23
|
+
gccmoderate: 'gcc',
|
|
24
|
+
gov: 'gcc',
|
|
25
|
+
gcchigh: 'usg',
|
|
26
|
+
high: 'usg',
|
|
27
|
+
dod: 'dod',
|
|
28
|
+
mooncake: 'china'
|
|
29
|
+
};
|
|
30
|
+
const ENVIRONMENT_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
31
|
+
function globalDiscoveryEndpoint(region) {
|
|
32
|
+
const cloud = REGION_DISCOVERY_CLOUD[region];
|
|
33
|
+
if (!cloud) {
|
|
34
|
+
throw new Error(`Dataverse Global Discovery is not available for Power Platform region "${region}"`);
|
|
35
|
+
}
|
|
36
|
+
return `https://${GLOBAL_DISCOVERY_HOST[cloud]}`;
|
|
37
|
+
}
|
|
38
|
+
function globalDiscoveryScope(region) {
|
|
39
|
+
return `${globalDiscoveryEndpoint(region)}/user_impersonation`;
|
|
40
|
+
}
|
|
41
|
+
function buildGlobalDiscoveryUrl(environmentId, region) {
|
|
42
|
+
if (!ENVIRONMENT_ID_PATTERN.test(environmentId)) {
|
|
43
|
+
throw new Error(`Invalid Power Platform environment ID: "${environmentId}"`);
|
|
44
|
+
}
|
|
45
|
+
const normalizedEnvironmentId = environmentId.toLowerCase();
|
|
46
|
+
const query = new URLSearchParams({
|
|
47
|
+
'$select': 'ApiUrl,EnvironmentId',
|
|
48
|
+
'$filter': `EnvironmentId eq '${normalizedEnvironmentId}'`
|
|
49
|
+
});
|
|
50
|
+
return `${globalDiscoveryEndpoint(region)}/api/discovery/v2.0/Instances?${query.toString()}`;
|
|
51
|
+
}
|
|
@@ -4,22 +4,13 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.POWER_PLATFORM_API_SCOPE = void 0;
|
|
7
|
-
exports.buildCdsDataSources = buildCdsDataSources;
|
|
8
7
|
exports.buildEnvironmentEndpoint = buildEnvironmentEndpoint;
|
|
9
|
-
exports.
|
|
8
|
+
exports.connectionUserConsentScope = connectionUserConsentScope;
|
|
10
9
|
exports.powerPlatformScope = powerPlatformScope;
|
|
11
|
-
var _index = require("../telemetry/index.js");
|
|
12
10
|
/*!
|
|
13
11
|
* power-apps-native-host — PowerPlatformEnvironmentApi
|
|
14
12
|
*
|
|
15
|
-
*
|
|
16
|
-
* pattern: resolves the Power Platform environment-scoped API endpoint from the
|
|
17
|
-
* environment GUID and region, then fetches linkedEnvironmentMetadata to obtain
|
|
18
|
-
* the Dataverse instanceUrl.
|
|
19
|
-
*
|
|
20
|
-
* Reference (Host SDK):
|
|
21
|
-
* packages/power-apps-host/src/providers/defaults/DefaultAppMetadataProvider.ts
|
|
22
|
-
* packages/power-apps-common/src/services/PowerPlatformApiDiscovery.ts
|
|
13
|
+
* Resolves Power Platform RP endpoints and scopes.
|
|
23
14
|
*/
|
|
24
15
|
|
|
25
16
|
// ---------------------------------------------------------------------------
|
|
@@ -78,10 +69,6 @@ function buildEnvironmentEndpoint(environmentId, region) {
|
|
|
78
69
|
return `https://${hexPrefix}.${hexSuffix}.environment.${hostNameSuffix}`;
|
|
79
70
|
}
|
|
80
71
|
|
|
81
|
-
// ---------------------------------------------------------------------------
|
|
82
|
-
// Public API
|
|
83
|
-
// ---------------------------------------------------------------------------
|
|
84
|
-
|
|
85
72
|
/**
|
|
86
73
|
* Token scope for the Power Platform management API (prod cluster).
|
|
87
74
|
* Non-prod clusters (test, gov, high, dev, mooncake, etc.) host the management
|
|
@@ -99,96 +86,9 @@ function powerPlatformScope(region) {
|
|
|
99
86
|
const host = HOST_NAME_SUFFIX[region] ?? HOST_NAME_SUFFIX['prod'];
|
|
100
87
|
return `https://${host}/.default`;
|
|
101
88
|
}
|
|
102
|
-
function getSafeErrorName(error) {
|
|
103
|
-
return error instanceof Error ? error.constructor.name : typeof error;
|
|
104
|
-
}
|
|
105
89
|
|
|
106
|
-
/**
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
* `databaseReferences[*].databaseDetails.linkedEnvironmentMetadata`.
|
|
111
|
-
*
|
|
112
|
-
* @param environmentId - Environment GUID (from power.config.json `environmentId`).
|
|
113
|
-
* @param region - Cluster region string (from power.config.json `region`, default "prod").
|
|
114
|
-
* @param acquireToken - Callback to obtain a bearer token for the given scopes.
|
|
115
|
-
* Scope will be {@link powerPlatformScope}`(region)`.
|
|
116
|
-
* @param bearerToken - Optional pre-fetched bearer token for {@link powerPlatformScope}`(region)`.
|
|
117
|
-
* Pass this to avoid a redundant `acquireToken` round-trip when the
|
|
118
|
-
* caller has already obtained a token for the same audience.
|
|
119
|
-
* @returns The Dataverse org URL (e.g. "https://org12345.crm.dynamics.com"), or
|
|
120
|
-
* `undefined` if the environment has no linked CDS database.
|
|
121
|
-
*/
|
|
122
|
-
async function fetchInstanceUrl(environmentId, region, acquireToken, bearerToken) {
|
|
123
|
-
const scenario = (0, _index.getAppLogger)().trackScenario(_index.HostOperationNames.PlatformFetchInstanceUrl, {
|
|
124
|
-
environmentId,
|
|
125
|
-
region
|
|
126
|
-
});
|
|
127
|
-
try {
|
|
128
|
-
const baseUrl = buildEnvironmentEndpoint(environmentId, region);
|
|
129
|
-
const url = `${baseUrl}/powerapps/environment?api-version=1&%24filter=name+eq+%27${encodeURIComponent(environmentId)}%27`;
|
|
130
|
-
const token = bearerToken ?? (await acquireToken([powerPlatformScope(region)]));
|
|
131
|
-
if (!token) {
|
|
132
|
-
throw new Error('fetchInstanceUrl: failed to acquire Power Platform API token');
|
|
133
|
-
}
|
|
134
|
-
const response = await fetch(url, {
|
|
135
|
-
method: 'GET',
|
|
136
|
-
headers: {
|
|
137
|
-
Authorization: `Bearer ${token}`,
|
|
138
|
-
'Content-Type': 'application/json'
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
if (!response.ok) {
|
|
142
|
-
throw new Error(`fetchInstanceUrl: environment API returned ${response.status} ${response.statusText}`);
|
|
143
|
-
}
|
|
144
|
-
const env = await response.json();
|
|
145
|
-
const instanceUrl = env.properties?.linkedEnvironmentMetadata?.instanceUrl;
|
|
146
|
-
scenario.complete({
|
|
147
|
-
hasInstanceUrl: instanceUrl !== undefined
|
|
148
|
-
});
|
|
149
|
-
return instanceUrl;
|
|
150
|
-
} catch (e) {
|
|
151
|
-
scenario.fail(undefined, {
|
|
152
|
-
errorName: getSafeErrorName(e)
|
|
153
|
-
});
|
|
154
|
-
throw e;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// ---------------------------------------------------------------------------
|
|
159
|
-
// buildCdsDataSources
|
|
160
|
-
// ---------------------------------------------------------------------------
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Shape of a single Dataverse table entry inside power.config.json
|
|
164
|
-
* `databaseReferences[*].dataSources[*]`.
|
|
165
|
-
*/
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Shape of a single database reference entry inside power.config.json
|
|
169
|
-
* `databaseReferences`.
|
|
170
|
-
*/
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Build the CDS data source config array that the SDK reads via
|
|
174
|
-
* AppPowerAppsClientPlugin.getAppCdsDataSourceConfigsAsync.
|
|
175
|
-
*
|
|
176
|
-
* Pass the resolved Dataverse org URL (from useConnectionRefs) and the
|
|
177
|
-
* databaseReferences map from power.config.json. The resulting array is
|
|
178
|
-
* passed directly to <PowerAppsHostProvider dataSources={...} />.
|
|
179
|
-
*
|
|
180
|
-
* @param databaseReferences - The `databaseReferences` map from power.config.json.
|
|
181
|
-
* @param dataverseOrgUrl - The resolved Dataverse org URL
|
|
182
|
-
* (connectionRefs['commondataserviceforapps']?.datasetName).
|
|
183
|
-
* Pass an empty string before the org URL is known;
|
|
184
|
-
* the host provider will receive empty runtimeUrl entries
|
|
185
|
-
* and the SDK will not attempt Dataverse calls.
|
|
186
|
-
*/
|
|
187
|
-
function buildCdsDataSources(databaseReferences, dataverseOrgUrl) {
|
|
188
|
-
return Object.values(databaseReferences).flatMap(dbRef => Object.values(dbRef.dataSources ?? {}).map(ds => ({
|
|
189
|
-
runtimeUrl: dataverseOrgUrl ? `${dataverseOrgUrl}/api/data/v9.0/` : '',
|
|
190
|
-
entitySetName: ds.entitySetName ?? '',
|
|
191
|
-
logicalName: ds.logicalName ?? '',
|
|
192
|
-
version: '9.2'
|
|
193
|
-
})));
|
|
90
|
+
/** Delegated permission required by the connection user-consent endpoint. */
|
|
91
|
+
function connectionUserConsentScope(region) {
|
|
92
|
+
const host = HOST_NAME_SUFFIX[region] ?? HOST_NAME_SUFFIX['prod'];
|
|
93
|
+
return `https://${host}/Connectivity.Connections.UserConsent`;
|
|
194
94
|
}
|
|
@@ -9,9 +9,14 @@ var _reactNative = require("react-native");
|
|
|
9
9
|
var _reactNativeSafeAreaContext = require("react-native-safe-area-context");
|
|
10
10
|
var _setupNativeHost = require("../bridge/setupNativeHost.js");
|
|
11
11
|
var _NativePowerAppsBridge = require("../bridge/NativePowerAppsBridge.js");
|
|
12
|
-
var _useConnectionRefs = require("../
|
|
12
|
+
var _useConnectionRefs = require("../connections/useConnectionRefs.js");
|
|
13
|
+
var _DataverseDataSources = require("../platform/DataverseDataSources.js");
|
|
13
14
|
var _PowerPlatformEnvironmentApi = require("../platform/PowerPlatformEnvironmentApi.js");
|
|
14
15
|
var _ConnectionSetupScreen = require("../connections/ConnectionSetupScreen.js");
|
|
16
|
+
var _types = require("../connections/types.js");
|
|
17
|
+
var _PowerPlatformConnectionApi = require("../connections/PowerPlatformConnectionApi.js");
|
|
18
|
+
var _connectionState = require("../connections/connectionState.js");
|
|
19
|
+
var _isDevPlayer = require("../utils/isDevPlayer.js");
|
|
15
20
|
var _logger = require("../logger.js");
|
|
16
21
|
var _index = require("../telemetry/index.js");
|
|
17
22
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
@@ -48,6 +53,7 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
|
|
|
48
53
|
// Entra ID token scope for the APIM connector proxy (API Hub).
|
|
49
54
|
const CONNECTOR_SCOPE = 'https://apihub.azure.com/.default';
|
|
50
55
|
const EMPTY_DATABASE_REFERENCES = {};
|
|
56
|
+
const EMPTY_CONNECTION_REFERENCES = {};
|
|
51
57
|
/**
|
|
52
58
|
* Initialises the NativePowerAppsBridge and manages the full pre-launch
|
|
53
59
|
* pipeline — connection resolution, disambiguation, and creation — before
|
|
@@ -58,6 +64,7 @@ const EMPTY_DATABASE_REFERENCES = {};
|
|
|
58
64
|
function PowerAppsHostProvider({
|
|
59
65
|
acquireToken,
|
|
60
66
|
isSignedIn,
|
|
67
|
+
onSignOut,
|
|
61
68
|
appId,
|
|
62
69
|
environmentId,
|
|
63
70
|
region = 'prod',
|
|
@@ -67,18 +74,75 @@ function PowerAppsHostProvider({
|
|
|
67
74
|
renderOAuthWebView,
|
|
68
75
|
isvClientId,
|
|
69
76
|
onDisambiguationNeeded,
|
|
77
|
+
onConnectionConsent,
|
|
78
|
+
onResourceConsent,
|
|
79
|
+
onConnectionConsentDeclined,
|
|
70
80
|
children
|
|
71
81
|
}) {
|
|
72
82
|
// Extra refs produced by ConnectionSetupScreen for newly-created connections.
|
|
73
83
|
const [extraConnectionRefs, setExtraConnectionRefs] = (0, _react.useState)({});
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const
|
|
84
|
+
const [consentApproved, setConsentApproved] = (0, _react.useState)(false);
|
|
85
|
+
const setupDone = consentApproved;
|
|
86
|
+
const renderConnectionError = (title, messages, useNeutralText = false) => /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
|
87
|
+
style: {
|
|
88
|
+
flex: 1,
|
|
89
|
+
padding: 24,
|
|
90
|
+
justifyContent: 'center',
|
|
91
|
+
backgroundColor: '#F8F8F8'
|
|
92
|
+
},
|
|
93
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
94
|
+
style: {
|
|
95
|
+
fontSize: 20,
|
|
96
|
+
fontWeight: '700',
|
|
97
|
+
color: '#1A1A1A',
|
|
98
|
+
marginBottom: 12
|
|
99
|
+
},
|
|
100
|
+
children: title
|
|
101
|
+
}), messages.map(message => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
102
|
+
style: {
|
|
103
|
+
fontSize: 14,
|
|
104
|
+
color: useNeutralText ? '#323130' : '#C4262E',
|
|
105
|
+
lineHeight: 20,
|
|
106
|
+
marginBottom: 8
|
|
107
|
+
},
|
|
108
|
+
children: message
|
|
109
|
+
}, message)), isSignedIn && onSignOut ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
|
110
|
+
accessibilityRole: "button",
|
|
111
|
+
onPress: () => {
|
|
112
|
+
void onSignOut();
|
|
113
|
+
},
|
|
114
|
+
style: {
|
|
115
|
+
alignSelf: 'flex-start',
|
|
116
|
+
backgroundColor: '#0F6CBD',
|
|
117
|
+
paddingHorizontal: 20,
|
|
118
|
+
paddingVertical: 12,
|
|
119
|
+
borderRadius: 4,
|
|
120
|
+
marginTop: 12
|
|
121
|
+
},
|
|
122
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
|
123
|
+
style: {
|
|
124
|
+
color: '#FFFFFF',
|
|
125
|
+
fontSize: 15,
|
|
126
|
+
fontWeight: '600'
|
|
127
|
+
},
|
|
128
|
+
children: "Sign out"
|
|
129
|
+
})
|
|
130
|
+
}) : null]
|
|
131
|
+
});
|
|
77
132
|
const {
|
|
78
133
|
connectionRefs,
|
|
79
134
|
isReady,
|
|
135
|
+
error: connectionError,
|
|
136
|
+
errorKind: connectionErrorKind,
|
|
80
137
|
missingConnectors,
|
|
81
|
-
|
|
138
|
+
missingConnectionReferences,
|
|
139
|
+
connectionRequirements,
|
|
140
|
+
resourceConsents: launchResourceConsents,
|
|
141
|
+
configurationErrors,
|
|
142
|
+
requiresConnectionConsent,
|
|
143
|
+
resolvedConfigKey,
|
|
144
|
+
dataverseOrgUrl,
|
|
145
|
+
databaseReferences: resolvedDatabaseReferences
|
|
82
146
|
} = (0, _useConnectionRefs.useConnectionRefs)({
|
|
83
147
|
isSignedIn,
|
|
84
148
|
acquireToken,
|
|
@@ -96,39 +160,43 @@ function PowerAppsHostProvider({
|
|
|
96
160
|
if (!isSignedIn) {
|
|
97
161
|
(0, _logger.debugLog)('[PAHost] user signed out — resetting bridge state');
|
|
98
162
|
setExtraConnectionRefs({});
|
|
163
|
+
setConsentApproved(false);
|
|
99
164
|
}
|
|
100
165
|
}, [isSignedIn]);
|
|
166
|
+
(0, _react.useEffect)(() => {
|
|
167
|
+
setExtraConnectionRefs({});
|
|
168
|
+
setConsentApproved(false);
|
|
169
|
+
}, [appId, environmentId, connectionReferences]);
|
|
101
170
|
|
|
102
171
|
// Merge hook refs with anything the setup screen created.
|
|
103
172
|
const mergedRefs = (0, _react.useMemo)(() => ({
|
|
104
173
|
...connectionRefs,
|
|
105
174
|
...extraConnectionRefs
|
|
106
175
|
}), [connectionRefs, extraConnectionRefs]);
|
|
107
|
-
const staticConnectionRefs = (0, _react.useMemo)(() => (0, _NativePowerAppsBridge.buildConnectionReferences)(connectionReferences), [connectionReferences]);
|
|
176
|
+
const staticConnectionRefs = (0, _react.useMemo)(() => _isDevPlayer.isDevPlayer ? (0, _NativePowerAppsBridge.buildConnectionReferences)(connectionReferences) : EMPTY_CONNECTION_REFERENCES, [connectionReferences, _isDevPlayer.isDevPlayer]);
|
|
108
177
|
|
|
109
178
|
// dataverseOrgUrl comes directly from useConnectionRefs (fetched from the
|
|
110
179
|
// environment metadata regardless of whether a commondataserviceforapps
|
|
111
180
|
// connection reference is configured). This allows Dataverse calls to work
|
|
112
181
|
// even when there is no explicit Dataverse connection in connectionReferences.
|
|
113
|
-
const cdsDataSources = (0, _react.useMemo)(() => (0,
|
|
182
|
+
const cdsDataSources = (0, _react.useMemo)(() => (0, _DataverseDataSources.buildCdsDataSources)(_isDevPlayer.isDevPlayer ? databaseReferences ?? EMPTY_DATABASE_REFERENCES : resolvedDatabaseReferences, dataverseOrgUrl), [databaseReferences, resolvedDatabaseReferences, dataverseOrgUrl, _isDevPlayer.isDevPlayer]);
|
|
114
183
|
|
|
115
184
|
// Build the missing-connector entries for ConnectionSetupScreen.
|
|
116
|
-
const missingEntries = (0, _react.useMemo)(() =>
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
};
|
|
122
|
-
}), [missingConnectors, connectionReferences]);
|
|
185
|
+
const missingEntries = (0, _react.useMemo)(() => missingConnectionReferences, [missingConnectionReferences]);
|
|
186
|
+
const resourceConsents = (0, _react.useMemo)(() => [...new Map(launchResourceConsents.map(resource => [resource.resourceId.toLowerCase(), resource])).values()], [launchResourceConsents]);
|
|
187
|
+
const currentConfigKey = (0, _react.useMemo)(() => (0, _types.buildConnectionResolutionKey)(appId, environmentId, _isDevPlayer.isDevPlayer ? connectionReferences : {}), [appId, environmentId, connectionReferences, _isDevPlayer.isDevPlayer]);
|
|
188
|
+
const resolutionMatchesCurrentConfig = resolvedConfigKey === currentConfigKey;
|
|
189
|
+
const needsConsent = (0, _connectionState.shouldShowConnectionReview)(_isDevPlayer.isDevPlayer, requiresConnectionConsent, resourceConsents.length > 0);
|
|
123
190
|
|
|
124
191
|
// Called by ConnectionSetupScreen once every missing connector is resolved.
|
|
125
|
-
const handleSetupComplete = (0, _react.useCallback)(resolvedIds => {
|
|
192
|
+
const handleSetupComplete = (0, _react.useCallback)(async resolvedIds => {
|
|
126
193
|
const refs = {};
|
|
127
|
-
for (const [
|
|
128
|
-
const
|
|
129
|
-
if (!
|
|
130
|
-
const
|
|
131
|
-
const
|
|
194
|
+
for (const [localRefId, connectionId] of Object.entries(resolvedIds)) {
|
|
195
|
+
const requirement = connectionRequirements.find(item => item.localRefId === localRefId);
|
|
196
|
+
if (!requirement) continue;
|
|
197
|
+
const apiId = requirement.apiId;
|
|
198
|
+
const dataSources = requirement.dataSources;
|
|
199
|
+
const runtimeUrl = dataSources.map(ds => connectionRefsRef.current[ds]?.runtimeUrl ?? (_isDevPlayer.isDevPlayer ? schemaMap[ds]?.properties?.primaryRuntimeUrl : undefined)).find(Boolean) ?? '';
|
|
132
200
|
for (const ds of dataSources) {
|
|
133
201
|
refs[ds] = {
|
|
134
202
|
apiId,
|
|
@@ -142,7 +210,44 @@ function PowerAppsHostProvider({
|
|
|
142
210
|
}
|
|
143
211
|
}
|
|
144
212
|
setExtraConnectionRefs(refs);
|
|
145
|
-
|
|
213
|
+
const mergedForConsent = {
|
|
214
|
+
...connectionRefsRef.current,
|
|
215
|
+
...refs
|
|
216
|
+
};
|
|
217
|
+
const usages = connectionRequirements.map(requirement => ({
|
|
218
|
+
localRefId: requirement.localRefId,
|
|
219
|
+
apiId: requirement.apiId,
|
|
220
|
+
connectionId: requirement.dataSources.map(dataSource => mergedForConsent[dataSource]?.connectionName).find(Boolean) ?? '',
|
|
221
|
+
dataSources: requirement.dataSources,
|
|
222
|
+
appActions: requirement.appActions ?? [],
|
|
223
|
+
apiScopes: requirement.apiScopes
|
|
224
|
+
}));
|
|
225
|
+
if (usages.some(usage => !usage.connectionId)) {
|
|
226
|
+
throw new Error('Not all required connections have been resolved.');
|
|
227
|
+
}
|
|
228
|
+
if (!_isDevPlayer.isDevPlayer && resourceConsents.length > 0 && !onResourceConsent) {
|
|
229
|
+
throw new Error('Protected resource consent requires an onResourceConsent persistence callback.');
|
|
230
|
+
}
|
|
231
|
+
const shouldPersistUsages = (0, _connectionState.shouldPersistConnectionUsages)(_isDevPlayer.isDevPlayer, appId);
|
|
232
|
+
if (shouldPersistUsages && appId && usages.length > 0) {
|
|
233
|
+
const token = await acquireToken([(0, _PowerPlatformEnvironmentApi.powerPlatformScope)(region)]);
|
|
234
|
+
if (!token) throw new Error('Unable to acquire a token to register connection usage.');
|
|
235
|
+
await Promise.all(usages.map(usage => (0, _PowerPlatformConnectionApi.postConnectionUsage)(appId, usage, environmentId, region, token)));
|
|
236
|
+
await (0, _PowerPlatformConnectionApi.reportScopeConsents)(appId, usages, environmentId, region, token);
|
|
237
|
+
}
|
|
238
|
+
if (!_isDevPlayer.isDevPlayer) {
|
|
239
|
+
await onConnectionConsent?.(usages);
|
|
240
|
+
await onResourceConsent?.(resourceConsents);
|
|
241
|
+
}
|
|
242
|
+
setConsentApproved(true);
|
|
243
|
+
}, [acquireToken, appId, environmentId, region, schemaMap, connectionRequirements, onConnectionConsent, onResourceConsent, resourceConsents, _isDevPlayer.isDevPlayer]);
|
|
244
|
+
const consentDetails = (0, _react.useMemo)(() => connectionRequirements.map(requirement => ({
|
|
245
|
+
localRefId: requirement.localRefId,
|
|
246
|
+
connectorId: requirement.connectorId,
|
|
247
|
+
displayName: requirement.displayName ?? requirement.connectorId.replace(/^shared_/, ''),
|
|
248
|
+
description: `Uses ${requirement.dataSources.join(', ')}`,
|
|
249
|
+
appActions: requirement.appActions ?? []
|
|
250
|
+
})), [connectionRequirements]);
|
|
146
251
|
|
|
147
252
|
// ---- Bridge wiring ----
|
|
148
253
|
|
|
@@ -154,7 +259,7 @@ function PowerAppsHostProvider({
|
|
|
154
259
|
connectionRefsRef.current = connectionRefs;
|
|
155
260
|
const configuredBridgeInputsRef = (0, _react.useRef)(undefined);
|
|
156
261
|
const [, setBridgeRegistrationVersion] = (0, _react.useState)(0);
|
|
157
|
-
const hasResolvedConnections = isSignedIn && isReady && (missingConnectors.length === 0 ||
|
|
262
|
+
const hasResolvedConnections = isSignedIn && isReady && resolutionMatchesCurrentConfig && (missingConnectors.length === 0 || consentApproved) && (!needsConsent || consentApproved);
|
|
158
263
|
const isBridgeConfiguredFor = (connections, dataSources) => {
|
|
159
264
|
const currentInputs = configuredBridgeInputsRef.current;
|
|
160
265
|
return currentInputs?.connections === connections && currentInputs.dataSources === dataSources;
|
|
@@ -254,13 +359,34 @@ function PowerAppsHostProvider({
|
|
|
254
359
|
}
|
|
255
360
|
}, [isSignedIn, isReady]);
|
|
256
361
|
(0, _react.useEffect)(() => {
|
|
257
|
-
if (isSignedIn && isReady && missingConnectors.length > 0 && !setupDone) {
|
|
362
|
+
if (isSignedIn && isReady && (missingConnectors.length > 0 || needsConsent) && !setupDone) {
|
|
258
363
|
(0, _logger.debugLog)(`[PAHost] render: showing ConnectionSetupScreen for ${missingConnectors.length} missing connector(s):`, missingConnectors.join(', '));
|
|
259
364
|
}
|
|
260
|
-
}, [isSignedIn, isReady, missingConnectors, setupDone]);
|
|
365
|
+
}, [isSignedIn, isReady, missingConnectors, needsConsent, setupDone]);
|
|
261
366
|
|
|
262
367
|
// ---- Render ----
|
|
263
368
|
|
|
369
|
+
if (connectionError) {
|
|
370
|
+
const accessDenied = connectionErrorKind === 'app-access-denied';
|
|
371
|
+
return renderConnectionError(accessDenied ? 'You don’t have access' : 'Unable to load app', [connectionError], accessDenied);
|
|
372
|
+
}
|
|
373
|
+
if (configurationErrors.length > 0) {
|
|
374
|
+
return renderConnectionError('Connection configuration error', configurationErrors);
|
|
375
|
+
}
|
|
376
|
+
if (isSignedIn && !resolutionMatchesCurrentConfig) {
|
|
377
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
|
|
378
|
+
style: {
|
|
379
|
+
flex: 1,
|
|
380
|
+
alignItems: 'center',
|
|
381
|
+
justifyContent: 'center',
|
|
382
|
+
backgroundColor: '#F8F8F8'
|
|
383
|
+
},
|
|
384
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
|
|
385
|
+
size: "large"
|
|
386
|
+
})
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
|
|
264
390
|
// First mount: register the native bridge with static manifest refs before
|
|
265
391
|
// any route children can mount and call into @microsoft/power-apps.
|
|
266
392
|
if (!staticBridgeRegistered && !hasResolvedConnections) {
|
|
@@ -296,7 +422,7 @@ function PowerAppsHostProvider({
|
|
|
296
422
|
// Wrapped in SafeAreaProvider so useSafeAreaInsets() works even when the
|
|
297
423
|
// consuming app hasn't mounted its own SafeAreaProvider yet. Nesting is
|
|
298
424
|
// safe — react-native-safe-area-context treats inner providers as no-ops.
|
|
299
|
-
if (isSignedIn && isReady && missingConnectors.length > 0 && !setupDone) {
|
|
425
|
+
if (isSignedIn && isReady && (missingConnectors.length > 0 || needsConsent) && !setupDone) {
|
|
300
426
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeSafeAreaContext.SafeAreaProvider, {
|
|
301
427
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_ConnectionSetupScreen.ConnectionSetupScreen, {
|
|
302
428
|
missingConnectors: missingEntries,
|
|
@@ -304,8 +430,13 @@ function PowerAppsHostProvider({
|
|
|
304
430
|
environmentId: environmentId,
|
|
305
431
|
region: region,
|
|
306
432
|
acquireToken: acquireToken,
|
|
433
|
+
onSignOut: isSignedIn ? onSignOut : undefined,
|
|
307
434
|
isvClientId: isvClientId,
|
|
308
435
|
onAllConnected: handleSetupComplete,
|
|
436
|
+
consentDetails: consentDetails,
|
|
437
|
+
resourceConsents: resourceConsents,
|
|
438
|
+
onConsentDeclined: onConnectionConsentDeclined,
|
|
439
|
+
showReview: !_isDevPlayer.isDevPlayer,
|
|
309
440
|
renderOAuthWebView: renderOAuthWebView
|
|
310
441
|
// No onSkip — connections are required.
|
|
311
442
|
})
|