@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
|
@@ -4,6 +4,16 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.ConnectionStatus = void 0;
|
|
7
|
+
exports.buildConnectionRequirements = buildConnectionRequirements;
|
|
8
|
+
exports.buildConnectionResolutionKey = buildConnectionResolutionKey;
|
|
9
|
+
exports.findConnectionDataSourceConflicts = findConnectionDataSourceConflicts;
|
|
10
|
+
exports.getConnectionDatasetName = getConnectionDatasetName;
|
|
11
|
+
exports.getConnectorId = getConnectorId;
|
|
12
|
+
exports.getFlowConnectionName = getFlowConnectionName;
|
|
13
|
+
exports.getFlowDataSourceNames = getFlowDataSourceNames;
|
|
14
|
+
exports.hasCompleteStaticConnectionConfig = hasCompleteStaticConnectionConfig;
|
|
15
|
+
exports.isCompositeConnectionApiId = isCompositeConnectionApiId;
|
|
16
|
+
exports.normalizeApiId = normalizeApiId;
|
|
7
17
|
/*!
|
|
8
18
|
* power-apps-native-host — connection disambiguation types
|
|
9
19
|
*
|
|
@@ -24,7 +34,100 @@ let ConnectionStatus = exports.ConnectionStatus = /*#__PURE__*/function (Connect
|
|
|
24
34
|
ConnectionStatus["NotConnected"] = "NotConnected";
|
|
25
35
|
return ConnectionStatus;
|
|
26
36
|
}({});
|
|
37
|
+
function normalizeApiId(apiId) {
|
|
38
|
+
const connectorId = apiId.split('/').filter(Boolean).pop() ?? apiId;
|
|
39
|
+
return `/providers/Microsoft.PowerApps/apis/${connectorId.toLowerCase()}`;
|
|
40
|
+
}
|
|
41
|
+
function getConnectorId(apiId) {
|
|
42
|
+
return (apiId.split('/').filter(Boolean).pop() ?? apiId).toLowerCase();
|
|
43
|
+
}
|
|
44
|
+
function getConnectionDatasetName(reference) {
|
|
45
|
+
const datasetNames = Object.keys(reference.dataSets ?? {});
|
|
46
|
+
return datasetNames.length === 1 ? datasetNames[0] : '';
|
|
47
|
+
}
|
|
48
|
+
function getFlowConnectionName(reference) {
|
|
49
|
+
const workflowNameHint = reference.parameterHintsV2?.workflowName?.value;
|
|
50
|
+
const workflowEntityIdHint = reference.parameterHintsV2?.workflowEntityId?.value;
|
|
51
|
+
return reference.workflowDetails?.workflowName ?? (typeof workflowNameHint === 'string' ? workflowNameHint : undefined) ?? reference.workflowDetails?.workflowEntityId ?? (typeof workflowEntityIdHint === 'string' ? workflowEntityIdHint : '');
|
|
52
|
+
}
|
|
53
|
+
function getFlowDataSourceNames(reference) {
|
|
54
|
+
const names = new Set(reference.dataSources);
|
|
55
|
+
const workflowDisplayNameHint = reference.parameterHintsV2?.workflowDisplayName?.value;
|
|
56
|
+
const displayName = reference.workflowDetails?.workflowDisplayName ?? (typeof workflowDisplayNameHint === 'string' ? workflowDisplayNameHint : undefined);
|
|
57
|
+
if (displayName) names.add(`${displayName.charAt(0).toLowerCase()}${displayName.slice(1)}`);
|
|
58
|
+
return [...names];
|
|
59
|
+
}
|
|
60
|
+
function buildConnectionRequirements(connectionReferences, scopesByApiId = {}) {
|
|
61
|
+
return Object.entries(connectionReferences).filter(([, reference]) => !isCompositeConnectionApiId(reference.id)).map(([localRefId, reference]) => {
|
|
62
|
+
const appActions = new Set(reference.appActions ?? []);
|
|
63
|
+
let isFlowDependent = false;
|
|
64
|
+
for (const dependentId of reference.dependents ?? []) {
|
|
65
|
+
const dependent = connectionReferences[dependentId];
|
|
66
|
+
if (!dependent || getConnectorId(dependent.id) !== 'shared_logicflows') continue;
|
|
67
|
+
isFlowDependent = true;
|
|
68
|
+
for (const action of dependent.nestedActions ?? []) {
|
|
69
|
+
const hint = action.flowConnectionRef ? dependent.parameterHintsV2?.[action.flowConnectionRef] : undefined;
|
|
70
|
+
if (hint?.value === localRefId && action.actionName) appActions.add(action.actionName);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const apiId = normalizeApiId(reference.id);
|
|
74
|
+
return {
|
|
75
|
+
localRefId,
|
|
76
|
+
apiId,
|
|
77
|
+
connectorId: getConnectorId(reference.id),
|
|
78
|
+
dataSources: reference.dataSources,
|
|
79
|
+
displayName: reference.displayName,
|
|
80
|
+
iconUri: reference.iconUri,
|
|
81
|
+
appActions: [...appActions],
|
|
82
|
+
sharedConnectionId: reference.sharedConnectionId,
|
|
83
|
+
gatewayObjectIdHint: reference.gatewayObjectIdHint,
|
|
84
|
+
authenticationType: reference.authenticationType,
|
|
85
|
+
parameterSetName: reference.parameterSetName,
|
|
86
|
+
apiScopes: scopesByApiId[apiId] ?? {
|
|
87
|
+
will: [],
|
|
88
|
+
wont: []
|
|
89
|
+
},
|
|
90
|
+
isFlowDependent
|
|
91
|
+
};
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
function isCompositeConnectionApiId(apiId) {
|
|
95
|
+
const connectorId = getConnectorId(apiId);
|
|
96
|
+
return connectorId === 'shared_excel' || connectorId === 'shared_logicflows';
|
|
97
|
+
}
|
|
98
|
+
function findConnectionDataSourceConflicts(requirements) {
|
|
99
|
+
const owners = new Map();
|
|
100
|
+
const conflicts = new Set();
|
|
101
|
+
for (const requirement of requirements) {
|
|
102
|
+
for (const dataSource of requirement.dataSources) {
|
|
103
|
+
const owner = owners.get(dataSource);
|
|
104
|
+
if (owner && owner !== requirement.localRefId) conflicts.add(dataSource);else owners.set(dataSource, requirement.localRefId);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return [...conflicts];
|
|
108
|
+
}
|
|
109
|
+
function hasCompleteStaticConnectionConfig(connectionReferences, schemaMap) {
|
|
110
|
+
const references = Object.values(connectionReferences);
|
|
111
|
+
const directReferences = references.filter(reference => !isCompositeConnectionApiId(reference.id));
|
|
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
|
+
}
|
|
114
|
+
function buildConnectionResolutionKey(appId, environmentId, connectionReferences) {
|
|
115
|
+
const references = Object.entries(connectionReferences).sort(([left], [right]) => left.localeCompare(right)).map(([localRefId, reference]) => ({
|
|
116
|
+
localRefId,
|
|
117
|
+
id: reference.id,
|
|
118
|
+
dataSources: [...reference.dataSources].sort(),
|
|
119
|
+
sharedConnectionId: reference.sharedConnectionId ?? '',
|
|
120
|
+
parameterSetName: reference.parameterSetName ?? ''
|
|
121
|
+
}));
|
|
122
|
+
return JSON.stringify({
|
|
123
|
+
appId: appId ?? '',
|
|
124
|
+
environmentId,
|
|
125
|
+
references
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
27
129
|
/** A single connection instance returned by the Power Platform connectivity API. */
|
|
130
|
+
|
|
28
131
|
/**
|
|
29
132
|
* Result of attempting to match a single connection reference to an instance.
|
|
30
133
|
*
|
|
@@ -32,10 +135,12 @@ let ConnectionStatus = exports.ConnectionStatus = /*#__PURE__*/function (Connect
|
|
|
32
135
|
* - `missing` — no connections found for this connector in the user's environment.
|
|
33
136
|
* - `ambiguous` — multiple Connected connections found; the app must pick one.
|
|
34
137
|
*/
|
|
138
|
+
|
|
35
139
|
/**
|
|
36
140
|
* Disambiguation result for a single connection reference (one apiId, possibly
|
|
37
141
|
* mapped to multiple data source names in the final connections map).
|
|
38
142
|
*/
|
|
143
|
+
|
|
39
144
|
/**
|
|
40
145
|
* Callback invoked when a connection reference has multiple Connected candidate
|
|
41
146
|
* connections. The consuming app should present a native picker UI (e.g. a
|
|
@@ -48,6 +153,7 @@ let ConnectionStatus = exports.ConnectionStatus = /*#__PURE__*/function (Connect
|
|
|
48
153
|
* @param context.apiId — connector API identifier
|
|
49
154
|
* @param context.candidates — list of Connected connection instances to choose from
|
|
50
155
|
*/
|
|
156
|
+
|
|
51
157
|
/**
|
|
52
158
|
* Callback invoked when the user has no connection for a required connector.
|
|
53
159
|
*
|
|
@@ -6,13 +6,14 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.useConnectionRefs = useConnectionRefs;
|
|
7
7
|
var _react = require("react");
|
|
8
8
|
var _isDevPlayer = require("../utils/isDevPlayer.js");
|
|
9
|
-
var _PowerPlatformEnvironmentApi = require("
|
|
10
|
-
var _types = require("../connections/types.js");
|
|
9
|
+
var _PowerPlatformEnvironmentApi = require("../platform/PowerPlatformEnvironmentApi.js");
|
|
11
10
|
var _PowerPlatformConnectionApi = require("./PowerPlatformConnectionApi.js");
|
|
12
11
|
var _resolveConnections = require("./resolveConnections.js");
|
|
12
|
+
var _connectionState = require("./connectionState.js");
|
|
13
13
|
var _logger = require("../logger.js");
|
|
14
14
|
var _index = require("../telemetry/index.js");
|
|
15
15
|
var _NativePowerAppsBridge = require("../bridge/NativePowerAppsBridge.js");
|
|
16
|
+
var _types = require("./types.js");
|
|
16
17
|
/*!
|
|
17
18
|
* power-apps-native-host — useConnectionRefs
|
|
18
19
|
*
|
|
@@ -54,17 +55,10 @@ var _NativePowerAppsBridge = require("../bridge/NativePowerAppsBridge.js");
|
|
|
54
55
|
* disambiguation support for environments where multiple connections exist for
|
|
55
56
|
* the same connector.
|
|
56
57
|
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* 2. Acquires a Power Platform API token.
|
|
62
|
-
* 3. In parallel: fetches the Dataverse instanceUrl and ALL connections for
|
|
63
|
-
* each configured connector.
|
|
64
|
-
* 4. Runs disambiguation: resolves single matches, invokes `onDisambiguationNeeded`
|
|
65
|
-
* for multiple matches, and invokes `onMissingConnection` for absent ones.
|
|
66
|
-
* 5. Pre-warms the Dataverse token in the MSAL cache.
|
|
67
|
-
* 6. Sets isReady=true with the fully-populated connections map.
|
|
58
|
+
* Outside Dev Player, the published app launch endpoint is authoritative.
|
|
59
|
+
* Dev Player resolves unpublished local references through per-connector APIs.
|
|
60
|
+
* Both paths disambiguate candidates, pre-warm Dataverse authentication, and
|
|
61
|
+
* return a fully populated bridge configuration.
|
|
68
62
|
*/
|
|
69
63
|
function useConnectionRefs({
|
|
70
64
|
isSignedIn,
|
|
@@ -81,31 +75,37 @@ function useConnectionRefs({
|
|
|
81
75
|
const [connectionRefs, setConnectionRefs] = (0, _react.useState)({});
|
|
82
76
|
const [isReady, setIsReady] = (0, _react.useState)(false);
|
|
83
77
|
const [error, setError] = (0, _react.useState)(null);
|
|
78
|
+
const [errorKind, setErrorKind] = (0, _react.useState)(null);
|
|
84
79
|
const [missingConnectors, setMissingConnectors] = (0, _react.useState)([]);
|
|
80
|
+
const [missingConnectionReferences, setMissingConnectionReferences] = (0, _react.useState)([]);
|
|
81
|
+
const [connectionRequirements, setConnectionRequirements] = (0, _react.useState)([]);
|
|
82
|
+
const [resourceConsents, setResourceConsents] = (0, _react.useState)([]);
|
|
83
|
+
const [configurationErrors, setConfigurationErrors] = (0, _react.useState)([]);
|
|
84
|
+
const [requiresConnectionConsent, setRequiresConnectionConsent] = (0, _react.useState)(false);
|
|
85
|
+
const [resolvedConfigKey, setResolvedConfigKey] = (0, _react.useState)(null);
|
|
85
86
|
const [dataverseOrgUrl, setDataverseOrgUrl] = (0, _react.useState)('');
|
|
87
|
+
const [databaseReferences, setDatabaseReferences] = (0, _react.useState)({});
|
|
86
88
|
|
|
87
|
-
// Bounded re-resolution for
|
|
88
|
-
// cold boot is usually a silent-auth miss (the account exists, but MSAL hasn't
|
|
89
|
-
// produced a token yet); without a retry the effect — keyed on isSignedIn —
|
|
90
|
-
// would never re-resolve once a token becomes available, leaving the bridge on
|
|
91
|
-
// the static fallback refs (no runtimeUrl) for the whole session. retryTick
|
|
92
|
-
// re-runs the effect; tokenRetryCountRef caps the attempts so it can never loop.
|
|
89
|
+
// Bounded re-resolution for transient silent-token misses.
|
|
93
90
|
const [retryTick, setRetryTick] = (0, _react.useState)(0);
|
|
94
91
|
const tokenRetryCountRef = (0, _react.useRef)(0);
|
|
95
|
-
//
|
|
96
|
-
// retry below (not an initial resolution or a sign-in). Used to avoid
|
|
97
|
-
// clobbering an already-ready UI: a retry must re-resolve in the background
|
|
98
|
-
// without dropping isReady, which would otherwise unmount the whole signed-in
|
|
99
|
-
// subtree (spinner) on every tick.
|
|
92
|
+
// Dev Player retries in the background without dropping its locally ready UI.
|
|
100
93
|
const isBackgroundRetryRef = (0, _react.useRef)(false);
|
|
101
94
|
const MAX_TOKEN_RETRIES = 5;
|
|
102
95
|
const TOKEN_RETRY_DELAY_MS = 2000;
|
|
96
|
+
const currentConfigKey = (0, _types.buildConnectionResolutionKey)(appId, environmentId, _isDevPlayer.isDevPlayer ? connectionReferences : {});
|
|
103
97
|
(0, _react.useEffect)(() => {
|
|
104
98
|
if (!isSignedIn) {
|
|
105
99
|
tokenRetryCountRef.current = 0;
|
|
106
100
|
isBackgroundRetryRef.current = false;
|
|
107
101
|
return;
|
|
108
102
|
}
|
|
103
|
+
if (!_isDevPlayer.isDevPlayer && (!appId || !environmentId)) {
|
|
104
|
+
setErrorKind('generic');
|
|
105
|
+
setError('The production app requires both an app ID and an environment ID before it can load from Power Platform.');
|
|
106
|
+
setIsReady(true);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
109
|
|
|
110
110
|
// Consume the background-retry flag once per run so a later initial run or
|
|
111
111
|
// sign-in can never inherit a stale value.
|
|
@@ -123,6 +123,7 @@ function useConnectionRefs({
|
|
|
123
123
|
// an online (offline-disabled) played app never resolves its instanceUrl in
|
|
124
124
|
// the Dev Player and every Dataverse read fails with "No instanceUrl found".
|
|
125
125
|
if (_isDevPlayer.isDevPlayer && !hasConnectionRefs && !environmentId) {
|
|
126
|
+
setResolvedConfigKey(currentConfigKey);
|
|
126
127
|
setIsReady(true);
|
|
127
128
|
return;
|
|
128
129
|
}
|
|
@@ -131,50 +132,63 @@ function useConnectionRefs({
|
|
|
131
132
|
// (and no appId to fetch from the launch API).
|
|
132
133
|
// Mark ready immediately so the bridge can initialise without any network calls.
|
|
133
134
|
if (!environmentId && !appId) {
|
|
135
|
+
setResolvedConfigKey(currentConfigKey);
|
|
134
136
|
setIsReady(true);
|
|
135
137
|
return;
|
|
136
138
|
}
|
|
137
139
|
let cancelled = false;
|
|
138
140
|
let retryTimer;
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
141
|
+
const resolutionScenario = (0, _index.getAppLogger)().trackScenario(_index.HostOperationNames.PlatformResolveConnectionRefs, {
|
|
142
|
+
mode: _isDevPlayer.isDevPlayer ? 'devPlayer' : 'publishedApp',
|
|
143
|
+
hasAppId: Boolean(appId),
|
|
144
|
+
connectionReferenceCount: Object.keys(connectionReferences).length
|
|
145
|
+
});
|
|
146
|
+
// Preserve the locally ready Dev Player UI during a background retry.
|
|
143
147
|
if (!isBackgroundRetry) {
|
|
144
148
|
setIsReady(false);
|
|
145
149
|
setError(null);
|
|
150
|
+
setErrorKind(null);
|
|
146
151
|
setMissingConnectors([]);
|
|
152
|
+
setMissingConnectionReferences([]);
|
|
153
|
+
setConnectionRequirements([]);
|
|
154
|
+
setResourceConsents([]);
|
|
155
|
+
setConfigurationErrors([]);
|
|
156
|
+
setRequiresConnectionConsent(false);
|
|
157
|
+
setResolvedConfigKey(null);
|
|
158
|
+
setDataverseOrgUrl('');
|
|
159
|
+
setDatabaseReferences({});
|
|
147
160
|
}
|
|
148
161
|
async function loadConnections() {
|
|
149
162
|
const token = await acquireToken([(0, _PowerPlatformEnvironmentApi.powerPlatformScope)(region)]);
|
|
150
|
-
if (cancelled)
|
|
163
|
+
if (cancelled) {
|
|
164
|
+
resolutionScenario.complete({
|
|
165
|
+
result: 'cancelled'
|
|
166
|
+
});
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
151
169
|
if (!token) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
// Still mark ready so the host mounts the UI (the offline SDK serves local
|
|
163
|
-
// SQLite without a token) instead of gating on !isReady forever.
|
|
164
|
-
setConnectionRefs((0, _NativePowerAppsBridge.buildConnectionReferences)(connectionReferences));
|
|
165
|
-
setIsReady(true);
|
|
166
|
-
// Schedule a bounded re-resolution so a transient silent-auth miss
|
|
167
|
-
// recovers once a token is available, instead of staying on the static
|
|
168
|
-
// fallback refs for the rest of the session. Capped by MAX_TOKEN_RETRIES
|
|
169
|
-
// so it can never spin.
|
|
170
|
+
resolutionScenario.completeWithError({
|
|
171
|
+
reason: 'tokenUnavailable'
|
|
172
|
+
});
|
|
173
|
+
// Dev Player can render local metadata while token recovery continues;
|
|
174
|
+
// standalone apps stay blocked because production has no static fallback.
|
|
175
|
+
if (_isDevPlayer.isDevPlayer) {
|
|
176
|
+
setConnectionRefs((0, _NativePowerAppsBridge.buildConnectionReferences)(connectionReferences));
|
|
177
|
+
setIsReady(true);
|
|
178
|
+
setResolvedConfigKey(currentConfigKey);
|
|
179
|
+
}
|
|
170
180
|
if (tokenRetryCountRef.current < MAX_TOKEN_RETRIES) {
|
|
171
181
|
tokenRetryCountRef.current += 1;
|
|
172
182
|
retryTimer = setTimeout(() => {
|
|
173
183
|
if (!cancelled) {
|
|
174
|
-
isBackgroundRetryRef.current =
|
|
184
|
+
isBackgroundRetryRef.current = _isDevPlayer.isDevPlayer;
|
|
175
185
|
setRetryTick(t => t + 1);
|
|
176
186
|
}
|
|
177
187
|
}, TOKEN_RETRY_DELAY_MS);
|
|
188
|
+
} else if (!_isDevPlayer.isDevPlayer) {
|
|
189
|
+
setErrorKind('generic');
|
|
190
|
+
setError('Unable to authenticate with Power Platform to load this app. Sign in again and retry.');
|
|
191
|
+
setIsReady(true);
|
|
178
192
|
}
|
|
179
193
|
return;
|
|
180
194
|
}
|
|
@@ -188,28 +202,47 @@ function useConnectionRefs({
|
|
|
188
202
|
let result = null;
|
|
189
203
|
if (appId && environmentId && !_isDevPlayer.isDevPlayer) {
|
|
190
204
|
result = await (0, _PowerPlatformConnectionApi.fetchAppLaunchData)(appId, environmentId, region, token);
|
|
205
|
+
if (!result) {
|
|
206
|
+
throw new Error(`App "${appId}" is not available in environment "${environmentId}". Push the mobile app to this environment and try again.`);
|
|
207
|
+
}
|
|
208
|
+
if (result && onDisambiguationNeeded) {
|
|
209
|
+
for (const requirement of [...result.missingConnectionReferences]) {
|
|
210
|
+
const candidates = result.connectionCandidates[requirement.localRefId] ?? [];
|
|
211
|
+
if (candidates.length < 2) continue;
|
|
212
|
+
const selectedId = await onDisambiguationNeeded({
|
|
213
|
+
localRefId: requirement.localRefId,
|
|
214
|
+
apiId: requirement.apiId,
|
|
215
|
+
candidates
|
|
216
|
+
});
|
|
217
|
+
if (selectedId) {
|
|
218
|
+
(0, _connectionState.selectLaunchConnection)(result, requirement.localRefId, selectedId);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
191
222
|
if (result && isvClientId) {
|
|
192
|
-
await (0, _resolveConnections.applyAclGrants)(result.connectionRefs, environmentId,
|
|
193
|
-
|
|
194
|
-
|
|
223
|
+
const failedConnectionNames = await (0, _resolveConnections.applyAclGrants)(result.connectionRefs, result.connectionRequirements, environmentId, region, acquireToken);
|
|
224
|
+
(0, _connectionState.invalidateFailedAclGrants)(result, failedConnectionNames);
|
|
225
|
+
} else if (result) {
|
|
226
|
+
(0, _logger.debugWarn)('[useConnectionRefs] could not grant user-selected connections; treating them as setup-required');
|
|
195
227
|
(0, _index.getAppLogger)().trackErrorEvent(_index.HostOperationNames.PlatformConnectionAclGrant, {
|
|
196
228
|
phase: 'launchAclVerify',
|
|
197
|
-
reason:
|
|
198
|
-
connectionRefCount:
|
|
229
|
+
reason: 'missingIsvClientId',
|
|
230
|
+
connectionRefCount: Object.keys(result.connectionRefs).length
|
|
199
231
|
});
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
206
|
-
}
|
|
232
|
+
const unresolved = new Set((0, _connectionState.getConnectionNamesRequiringAclGrant)(result.connectionRefs, result.connectionRequirements));
|
|
233
|
+
(0, _connectionState.invalidateFailedAclGrants)(result, unresolved);
|
|
234
|
+
}
|
|
235
|
+
if (result) {
|
|
236
|
+
(0, _connectionState.applyRegisteredConnectionUsages)(result, result.registeredConnectionUsages);
|
|
207
237
|
}
|
|
208
238
|
}
|
|
209
239
|
|
|
210
240
|
// Slow path: per-connector resolution.
|
|
211
|
-
// Used
|
|
241
|
+
// Used only in Dev Player, where unpublished local app metadata is expected.
|
|
212
242
|
if (!result) {
|
|
243
|
+
if (!_isDevPlayer.isDevPlayer) {
|
|
244
|
+
throw new Error('Unable to load the production app from the Power Platform launch endpoint.');
|
|
245
|
+
}
|
|
213
246
|
result = await (0, _resolveConnections.resolveViaStaticRefs)({
|
|
214
247
|
connectionReferences,
|
|
215
248
|
schemaMap,
|
|
@@ -222,7 +255,12 @@ function useConnectionRefs({
|
|
|
222
255
|
onMissingConnection
|
|
223
256
|
});
|
|
224
257
|
}
|
|
225
|
-
if (cancelled)
|
|
258
|
+
if (cancelled) {
|
|
259
|
+
resolutionScenario.complete({
|
|
260
|
+
result: 'cancelled'
|
|
261
|
+
});
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
226
264
|
|
|
227
265
|
// Pre-warm the Dataverse token while the user waits.
|
|
228
266
|
if (result.dataverseOrgUrl) {
|
|
@@ -231,8 +269,20 @@ function useConnectionRefs({
|
|
|
231
269
|
if (!cancelled) {
|
|
232
270
|
setConnectionRefs(result.connectionRefs);
|
|
233
271
|
setMissingConnectors(result.missingConnectors);
|
|
272
|
+
setMissingConnectionReferences(result.missingConnectionReferences);
|
|
273
|
+
setConnectionRequirements(result.connectionRequirements);
|
|
274
|
+
setResourceConsents(result.resourceConsents);
|
|
275
|
+
setConfigurationErrors(result.configurationErrors);
|
|
276
|
+
setRequiresConnectionConsent(result.requiresConnectionConsent);
|
|
234
277
|
setDataverseOrgUrl(result.dataverseOrgUrl);
|
|
278
|
+
setDatabaseReferences(result.databaseReferences);
|
|
279
|
+
setResolvedConfigKey(currentConfigKey);
|
|
235
280
|
setIsReady(true);
|
|
281
|
+
resolutionScenario.complete({
|
|
282
|
+
result: 'resolved',
|
|
283
|
+
missingConnectionReferenceCount: result.missingConnectionReferences.length,
|
|
284
|
+
configurationErrorCount: result.configurationErrors.length
|
|
285
|
+
});
|
|
236
286
|
}
|
|
237
287
|
}
|
|
238
288
|
loadConnections().catch(e => {
|
|
@@ -243,6 +293,9 @@ function useConnectionRefs({
|
|
|
243
293
|
// production without raising a __DEV__ LogBox redbox over the
|
|
244
294
|
// otherwise-working UI. We still recover via setIsReady below.
|
|
245
295
|
(0, _logger.debugLog)('[useConnectionRefs] Failed to load connections:', e);
|
|
296
|
+
resolutionScenario.fail(undefined, {
|
|
297
|
+
errorName: e instanceof Error ? e.name : 'UnknownError'
|
|
298
|
+
});
|
|
246
299
|
(0, _index.getAppLogger)().trackErrorEvent(_index.HostOperationNames.PlatformLoadConnectionsFailed, {
|
|
247
300
|
error: String(e),
|
|
248
301
|
hasAppId: Boolean(appId),
|
|
@@ -250,7 +303,9 @@ function useConnectionRefs({
|
|
|
250
303
|
connectionReferenceCount: Object.keys(connectionReferences).length,
|
|
251
304
|
runningInDevPlayer: _isDevPlayer.isDevPlayer
|
|
252
305
|
});
|
|
253
|
-
|
|
306
|
+
const accessDenied = e instanceof _PowerPlatformConnectionApi.AppLaunchAccessDeniedError;
|
|
307
|
+
setErrorKind(accessDenied ? 'app-access-denied' : 'generic');
|
|
308
|
+
setError(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.');
|
|
254
309
|
setIsReady(true);
|
|
255
310
|
}
|
|
256
311
|
});
|
|
@@ -259,12 +314,20 @@ function useConnectionRefs({
|
|
|
259
314
|
if (retryTimer) clearTimeout(retryTimer);
|
|
260
315
|
};
|
|
261
316
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
262
|
-
}, [isSignedIn, retryTick]);
|
|
317
|
+
}, [isSignedIn, retryTick, appId, environmentId, region, connectionReferences, schemaMap, isvClientId]);
|
|
263
318
|
return {
|
|
264
319
|
connectionRefs,
|
|
265
320
|
isReady,
|
|
266
321
|
error,
|
|
322
|
+
errorKind,
|
|
267
323
|
missingConnectors,
|
|
268
|
-
|
|
324
|
+
missingConnectionReferences,
|
|
325
|
+
connectionRequirements,
|
|
326
|
+
resourceConsents,
|
|
327
|
+
configurationErrors,
|
|
328
|
+
requiresConnectionConsent,
|
|
329
|
+
resolvedConfigKey,
|
|
330
|
+
dataverseOrgUrl,
|
|
331
|
+
databaseReferences
|
|
269
332
|
};
|
|
270
333
|
}
|