@microsoft/power-apps-native-host 0.2.25 → 0.3.3
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 -10
- package/compatibility/current.json +31 -4
- package/lib/NOTICE.txt +1325 -24
- package/lib/commonjs/auth/AuthContext.js +110 -33
- 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/config/babelConfig.js +13 -0
- package/lib/commonjs/config/expoConfig.js +71 -0
- package/lib/commonjs/config/metroConfig.js +70 -0
- package/lib/commonjs/config/metroLogger.js +182 -0
- package/lib/commonjs/config/tamaguiConfig.js +98 -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 +348 -0
- package/lib/commonjs/connections/types.js +106 -0
- package/lib/commonjs/{platform → connections}/useConnectionRefs.js +130 -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 +167 -4
- package/lib/commonjs/logger.js +9 -6
- package/lib/commonjs/offline/createOfflineHostActivationCallbacks.js +61 -0
- package/lib/commonjs/offline/useOfflineIntegrationWithStartupGate.js +48 -0
- package/lib/commonjs/offline/useOfflineIntegrationWithStartupGate.web.js +13 -0
- package/lib/commonjs/platform/DataverseDataSources.js +18 -0
- package/lib/commonjs/platform/DataverseGlobalDiscoveryApi.js +94 -0
- package/lib/commonjs/platform/GlobalDiscovery.js +51 -0
- package/lib/commonjs/platform/PowerPlatformEnvironmentApi.js +6 -106
- package/lib/commonjs/provider/PowerAppsHostProvider.js +157 -25
- package/lib/commonjs/provider/PowerAppsProvider.js +178 -134
- package/lib/commonjs/provider/SessionDetailsGesture.js +208 -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 +110 -33
- 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/config/babelConfig.js +9 -0
- package/lib/module/config/expoConfig.js +67 -0
- package/lib/module/config/metroConfig.js +65 -0
- package/lib/module/config/metroLogger.js +175 -0
- package/lib/module/config/tamaguiConfig.js +93 -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 +343 -0
- package/lib/module/connections/types.js +91 -0
- package/lib/module/{platform → connections}/useConnectionRefs.js +131 -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 +14 -4
- package/lib/module/logger.js +8 -6
- package/lib/module/offline/createOfflineHostActivationCallbacks.js +55 -0
- package/lib/module/offline/useOfflineIntegrationWithStartupGate.js +44 -0
- package/lib/module/offline/useOfflineIntegrationWithStartupGate.web.js +9 -0
- package/lib/module/platform/DataverseDataSources.js +13 -0
- package/lib/module/platform/DataverseGlobalDiscoveryApi.js +89 -0
- package/lib/module/platform/GlobalDiscovery.js +45 -0
- package/lib/module/platform/PowerPlatformEnvironmentApi.js +5 -104
- package/lib/module/provider/PowerAppsHostProvider.js +160 -28
- package/lib/module/provider/PowerAppsProvider.js +182 -138
- package/lib/module/provider/SessionDetailsGesture.js +203 -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/config/babelConfig.d.ts +12 -0
- package/lib/typescript/commonjs/config/expoConfig.d.ts +21 -0
- package/lib/typescript/commonjs/config/metroConfig.d.ts +6 -0
- package/lib/typescript/commonjs/config/metroLogger.d.ts +21 -0
- package/lib/typescript/commonjs/config/tamaguiConfig.d.ts +1792 -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 +22 -20
- package/lib/typescript/commonjs/connections/types.d.ts +129 -1
- package/lib/typescript/commonjs/{platform → connections}/useConnectionRefs.d.ts +36 -25
- 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 +18 -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 +28 -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 +54 -9
- 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/config/babelConfig.d.ts +12 -0
- package/lib/typescript/module/config/expoConfig.d.ts +21 -0
- package/lib/typescript/module/config/metroConfig.d.ts +6 -0
- package/lib/typescript/module/config/metroLogger.d.ts +21 -0
- package/lib/typescript/module/config/tamaguiConfig.d.ts +1792 -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 +22 -20
- package/lib/typescript/module/connections/types.d.ts +129 -1
- package/lib/typescript/module/{platform → connections}/useConnectionRefs.d.ts +36 -25
- 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 +18 -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 +28 -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 +54 -9
- 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 +52 -13
- package/scripts/build-codegen-package.js +283 -0
- package/scripts/start-web.mjs +9 -5
- package/scripts/upgrade/runtime.js +139 -24
- package/scripts/upgrade-authoring.js +274 -180
- package/scripts/upgrade.js +10 -16
- package/src/config/generated-config.d.ts +4 -0
- package/src/config/index.d.ts +3 -0
- package/src/config/tsconfig.json +26 -0
- 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,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.buildConnectionParameterSets = buildConnectionParameterSets;
|
|
7
|
+
exports.buildConnectionUsageRequest = buildConnectionUsageRequest;
|
|
8
|
+
exports.buildConnectionUsageUrl = buildConnectionUsageUrl;
|
|
9
|
+
exports.buildConnectionUserConsentUrl = buildConnectionUserConsentUrl;
|
|
10
|
+
exports.buildConsentLinkDefinition = buildConsentLinkDefinition;
|
|
11
|
+
exports.buildScopeConsentRequest = buildScopeConsentRequest;
|
|
12
|
+
exports.buildScopeConsentUrl = buildScopeConsentUrl;
|
|
13
|
+
exports.getAppLaunchFailureMessage = getAppLaunchFailureMessage;
|
|
14
|
+
exports.hasOAuthParameter = hasOAuthParameter;
|
|
15
|
+
exports.isAppLaunchAccessDenied = isAppLaunchAccessDenied;
|
|
16
|
+
exports.parseConnectionParameters = parseConnectionParameters;
|
|
17
|
+
exports.serializeConnectionParameters = serializeConnectionParameters;
|
|
18
|
+
var _types = require("./types.js");
|
|
19
|
+
function parseConnectionParameters(parameters) {
|
|
20
|
+
return Object.entries(parameters ?? {}).flatMap(([name, parameter]) => {
|
|
21
|
+
if (!parameter.uiDefinition) return [];
|
|
22
|
+
const constraints = parameter.uiDefinition.constraints;
|
|
23
|
+
const type = (parameter.type ?? 'string').toLowerCase();
|
|
24
|
+
const supportedType = type === 'string' || type === 'securestring' || type === 'boolean' || type === 'integer' || type === 'clientcertificate' ? type : null;
|
|
25
|
+
if (!supportedType) return [];
|
|
26
|
+
return [{
|
|
27
|
+
name,
|
|
28
|
+
displayName: parameter.uiDefinition.displayName ?? name,
|
|
29
|
+
type: supportedType,
|
|
30
|
+
required: constraints?.required === true || constraints?.required === 'true',
|
|
31
|
+
hidden: constraints?.hidden === true || constraints?.hidden === 'true',
|
|
32
|
+
capabilities: constraints?.capability,
|
|
33
|
+
allowedValues: constraints?.allowedValues?.filter(item => Boolean(item.value)).map(item => ({
|
|
34
|
+
value: item.value,
|
|
35
|
+
displayName: item.displayName ?? item.value
|
|
36
|
+
})),
|
|
37
|
+
defaultValue: parameter.defaultValue
|
|
38
|
+
}];
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function buildConnectionParameterSets(connectionParameters, parameterSets) {
|
|
42
|
+
const declaredSets = (parameterSets ?? []).filter(item => Boolean(item.name)).map(item => ({
|
|
43
|
+
name: item.name,
|
|
44
|
+
displayName: item.uiDefinition?.displayName ?? item.name,
|
|
45
|
+
parameters: parseConnectionParameters(item.connectionParameters ?? item.parameters),
|
|
46
|
+
requiresApiConsent: hasOAuthParameter(item.connectionParameters ?? item.parameters),
|
|
47
|
+
gatewayParameterName: Object.entries(item.connectionParameters ?? item.parameters ?? {}).find(([, parameter]) => parameter.type?.toLowerCase() === 'gatewaysetting')?.[0]
|
|
48
|
+
}));
|
|
49
|
+
return declaredSets.length > 0 ? declaredSets : [{
|
|
50
|
+
name: undefined,
|
|
51
|
+
displayName: 'Default',
|
|
52
|
+
parameters: parseConnectionParameters(connectionParameters),
|
|
53
|
+
requiresApiConsent: hasOAuthParameter(connectionParameters),
|
|
54
|
+
gatewayParameterName: Object.entries(connectionParameters ?? {}).find(([, parameter]) => parameter.type?.toLowerCase() === 'gatewaysetting')?.[0]
|
|
55
|
+
}];
|
|
56
|
+
}
|
|
57
|
+
function hasOAuthParameter(parameters) {
|
|
58
|
+
return Object.values(parameters ?? {}).some(parameter => parameter.type?.toLowerCase() === 'oauthsetting');
|
|
59
|
+
}
|
|
60
|
+
function buildConnectionUserConsentUrl(environmentEndpoint, connectorId, connectionId) {
|
|
61
|
+
return `${environmentEndpoint}/connectivity/connectors/${encodeURIComponent((0, _types.getConnectorId)(connectorId))}` + `/connections/${encodeURIComponent(connectionId)}/userConsent?api-version=1`;
|
|
62
|
+
}
|
|
63
|
+
function getAppLaunchFailureMessage(status, statusText, responseText) {
|
|
64
|
+
if (status === 404) return null;
|
|
65
|
+
return `Unable to load the published app (HTTP ${status}): ${responseText || statusText}`;
|
|
66
|
+
}
|
|
67
|
+
function isAppLaunchAccessDenied(status, responseText) {
|
|
68
|
+
if (status !== 403) return false;
|
|
69
|
+
try {
|
|
70
|
+
const body = JSON.parse(responseText);
|
|
71
|
+
return body.error?.code === 'PowerAppsNoAccess' || body.error?.details?.some(detail => detail.code === 'PowerAppsNoAccess') === true;
|
|
72
|
+
} catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function buildConsentLinkDefinition(redirectUrl) {
|
|
77
|
+
return {
|
|
78
|
+
redirectUrl
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function serializeConnectionParameters(input) {
|
|
82
|
+
const parameters = {
|
|
83
|
+
...(input?.parameters ?? {}),
|
|
84
|
+
...(input?.gatewayObjectId && input.gatewayParameterName ? {
|
|
85
|
+
[input.gatewayParameterName]: {
|
|
86
|
+
gatewayObjectId: input.gatewayObjectId
|
|
87
|
+
}
|
|
88
|
+
} : {})
|
|
89
|
+
};
|
|
90
|
+
if (!input?.parameterSetName) return {
|
|
91
|
+
connectionParameters: parameters
|
|
92
|
+
};
|
|
93
|
+
return {
|
|
94
|
+
connectionParametersSet: {
|
|
95
|
+
name: input.parameterSetName,
|
|
96
|
+
values: Object.fromEntries(Object.entries(parameters).map(([name, value]) => [name, {
|
|
97
|
+
value
|
|
98
|
+
}]))
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function buildConnectionUsageUrl(environmentBaseUrl, environmentId, usage) {
|
|
103
|
+
return `${environmentBaseUrl}/powerapps/apis/${encodeURIComponent((0, _types.getConnectorId)(usage.apiId))}` + `/connections/${encodeURIComponent(usage.connectionId)}/usages` + `?api-version=1&$filter=environment eq '${environmentId}'`;
|
|
104
|
+
}
|
|
105
|
+
function buildConnectionUsageRequest(appId, usage) {
|
|
106
|
+
const resourceId = appId.startsWith('/providers/') ? appId : `/providers/Microsoft.PowerApps/apps/${appId}`;
|
|
107
|
+
return {
|
|
108
|
+
properties: {
|
|
109
|
+
reference: usage.localRefId,
|
|
110
|
+
resourceId,
|
|
111
|
+
appActions: usage.appActions
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function buildScopeConsentUrl(environmentBaseUrl, appId) {
|
|
116
|
+
const appName = appId.split('/').filter(Boolean).pop() ?? appId;
|
|
117
|
+
return `${environmentBaseUrl}/powerapps/apps/${encodeURIComponent(appName)}` + '/reportScopesConsent?api-version=1';
|
|
118
|
+
}
|
|
119
|
+
function buildScopeConsentRequest(usages) {
|
|
120
|
+
return {
|
|
121
|
+
consents: usages.map(usage => ({
|
|
122
|
+
connectionId: usage.connectionId,
|
|
123
|
+
consentType: 'userConsent',
|
|
124
|
+
scopes: usage.apiScopes
|
|
125
|
+
}))
|
|
126
|
+
};
|
|
127
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.applyRegisteredConnectionSelections = applyRegisteredConnectionSelections;
|
|
7
|
+
exports.applyRegisteredConnectionUsages = applyRegisteredConnectionUsages;
|
|
8
|
+
exports.getConnectionNamesRequiringAclGrant = getConnectionNamesRequiringAclGrant;
|
|
9
|
+
exports.getUnregisteredConnectionRequirements = getUnregisteredConnectionRequirements;
|
|
10
|
+
exports.hasRegisteredConnectionConsent = hasRegisteredConnectionConsent;
|
|
11
|
+
exports.invalidateFailedAclGrants = invalidateFailedAclGrants;
|
|
12
|
+
exports.isTransientConnectionConsentStatus = isTransientConnectionConsentStatus;
|
|
13
|
+
exports.selectLaunchConnection = selectLaunchConnection;
|
|
14
|
+
exports.shouldGrantConnectionUserConsent = shouldGrantConnectionUserConsent;
|
|
15
|
+
exports.shouldPersistConnectionUsages = shouldPersistConnectionUsages;
|
|
16
|
+
exports.shouldShowConnectionReview = shouldShowConnectionReview;
|
|
17
|
+
var _types = require("./types.js");
|
|
18
|
+
var _disambiguate = require("./disambiguate.js");
|
|
19
|
+
function isTransientConnectionConsentStatus(status) {
|
|
20
|
+
return status === 408 || status === 429 || status >= 500;
|
|
21
|
+
}
|
|
22
|
+
function shouldGrantConnectionUserConsent(requirement, runningInDevPlayer) {
|
|
23
|
+
return runningInDevPlayer || !requirement.sharedConnectionId;
|
|
24
|
+
}
|
|
25
|
+
function getConnectionNamesRequiringAclGrant(connectionRefs, connectionRequirements) {
|
|
26
|
+
const connectionNames = [];
|
|
27
|
+
const seen = new Set();
|
|
28
|
+
for (const requirement of connectionRequirements) {
|
|
29
|
+
if (!shouldGrantConnectionUserConsent(requirement, false)) continue;
|
|
30
|
+
const connectionName = requirement.dataSources.map(dataSource => connectionRefs[dataSource]?.connectionName).find(selectedName => Boolean(selectedName));
|
|
31
|
+
if (!connectionName || seen.has(connectionName)) continue;
|
|
32
|
+
seen.add(connectionName);
|
|
33
|
+
connectionNames.push(connectionName);
|
|
34
|
+
}
|
|
35
|
+
return connectionNames;
|
|
36
|
+
}
|
|
37
|
+
function invalidateFailedAclGrants(result, failedConnectionNames) {
|
|
38
|
+
if (failedConnectionNames.size === 0) return;
|
|
39
|
+
for (const requirement of result.connectionRequirements) {
|
|
40
|
+
const selected = requirement.dataSources.map(dataSource => result.connectionRefs[dataSource]).find(Boolean);
|
|
41
|
+
if (!selected || !failedConnectionNames.has(selected.connectionName)) continue;
|
|
42
|
+
for (const dataSource of requirement.dataSources) {
|
|
43
|
+
const reference = result.connectionRefs[dataSource];
|
|
44
|
+
if (reference) reference.connectionName = '';
|
|
45
|
+
}
|
|
46
|
+
if (!result.missingConnectionReferences.some(item => item.localRefId === requirement.localRefId)) {
|
|
47
|
+
result.missingConnectionReferences.push(requirement);
|
|
48
|
+
}
|
|
49
|
+
if (!result.missingConnectors.includes(requirement.apiId)) {
|
|
50
|
+
result.missingConnectors.push(requirement.apiId);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function selectLaunchConnection(result, localRefId, connectionId) {
|
|
55
|
+
const requirement = result.connectionRequirements.find(item => item.localRefId === localRefId);
|
|
56
|
+
const candidate = result.connectionCandidates[localRefId]?.find(item => (0, _disambiguate.connectionIdsEqual)(item.id, connectionId));
|
|
57
|
+
if (!requirement || !candidate) return false;
|
|
58
|
+
for (const dataSource of requirement.dataSources) {
|
|
59
|
+
const reference = result.connectionRefs[dataSource];
|
|
60
|
+
if (reference) reference.connectionName = candidate.id;
|
|
61
|
+
}
|
|
62
|
+
result.missingConnectionReferences = result.missingConnectionReferences.filter(item => item.localRefId !== localRefId);
|
|
63
|
+
if (!result.missingConnectionReferences.some(item => item.apiId === requirement.apiId)) {
|
|
64
|
+
result.missingConnectors = result.missingConnectors.filter(apiId => apiId !== requirement.apiId);
|
|
65
|
+
}
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
function getUnregisteredConnectionRequirements(result, usages = result.registeredConnectionUsages) {
|
|
69
|
+
return result.connectionRequirements.filter(requirement => {
|
|
70
|
+
const selectedConnectionId = requirement.dataSources.map(dataSource => result.connectionRefs[dataSource]?.connectionName).find(Boolean);
|
|
71
|
+
if (!selectedConnectionId) return true;
|
|
72
|
+
return !usages.some(usage => usage.reference.toLowerCase() === requirement.localRefId.toLowerCase() && (0, _types.normalizeApiId)(usage.apiId) === requirement.apiId && (0, _disambiguate.connectionIdsEqual)(usage.connectionId, selectedConnectionId) && (usage.appActions.length === 0 || (requirement.appActions ?? []).every(action => usage.appActions.includes(action))));
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function applyRegisteredConnectionSelections(result, usages) {
|
|
76
|
+
for (const requirement of result.connectionRequirements) {
|
|
77
|
+
const usage = usages.find(item => item.reference.toLowerCase() === requirement.localRefId.toLowerCase() && (0, _types.normalizeApiId)(item.apiId) === requirement.apiId);
|
|
78
|
+
if (!usage) continue;
|
|
79
|
+
const candidate = result.connectionCandidates[requirement.localRefId]?.find(item => (0, _disambiguate.connectionIdsEqual)(item.id, usage.connectionId));
|
|
80
|
+
if (candidate) selectLaunchConnection(result, requirement.localRefId, candidate.id);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function hasRegisteredConnectionConsent(result, usages = result.registeredConnectionUsages) {
|
|
84
|
+
return getUnregisteredConnectionRequirements(result, usages).length === 0;
|
|
85
|
+
}
|
|
86
|
+
function applyRegisteredConnectionUsages(result, usages) {
|
|
87
|
+
applyRegisteredConnectionSelections(result, usages);
|
|
88
|
+
result.registeredConnectionUsages = usages;
|
|
89
|
+
result.requiresConnectionConsent = getUnregisteredConnectionRequirements(result, usages).length > 0;
|
|
90
|
+
}
|
|
91
|
+
function shouldPersistConnectionUsages(runningInDevPlayer, appId) {
|
|
92
|
+
return !runningInDevPlayer && Boolean(appId);
|
|
93
|
+
}
|
|
94
|
+
function shouldShowConnectionReview(runningInDevPlayer, requiresConnectionConsent, hasResourceConsents) {
|
|
95
|
+
return !runningInDevPlayer && (requiresConnectionConsent || hasResourceConsents);
|
|
96
|
+
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
+
exports.connectionIdsEqual = connectionIdsEqual;
|
|
6
7
|
exports.disambiguateConnections = disambiguateConnections;
|
|
7
8
|
exports.extractConnectionId = extractConnectionId;
|
|
8
9
|
var _types = require("./types.js");
|
|
@@ -35,11 +36,20 @@ var _types = require("./types.js");
|
|
|
35
36
|
function disambiguateConnections(connectionReferences, allConnectionsByApiId) {
|
|
36
37
|
const resolutions = [];
|
|
37
38
|
for (const [localRefId, ref] of Object.entries(connectionReferences)) {
|
|
38
|
-
const apiId = ref.id;
|
|
39
|
-
const all = allConnectionsByApiId[apiId] ?? [];
|
|
39
|
+
const apiId = (0, _types.normalizeApiId)(ref.id);
|
|
40
|
+
const all = allConnectionsByApiId[apiId] ?? allConnectionsByApiId[ref.id] ?? [];
|
|
40
41
|
const connected = all.filter(c => c.status === _types.ConnectionStatus.Connected);
|
|
41
42
|
let result;
|
|
42
|
-
if (
|
|
43
|
+
if (ref.sharedConnectionId) {
|
|
44
|
+
const sharedId = extractConnectionId(ref.sharedConnectionId);
|
|
45
|
+
const shared = connected.find(connection => connectionIdsEqual(connection.id, sharedId));
|
|
46
|
+
result = shared ? {
|
|
47
|
+
kind: 'resolved',
|
|
48
|
+
connectionId: shared.id
|
|
49
|
+
} : {
|
|
50
|
+
kind: 'missing'
|
|
51
|
+
};
|
|
52
|
+
} else if (connected.length === 1) {
|
|
43
53
|
result = {
|
|
44
54
|
kind: 'resolved',
|
|
45
55
|
connectionId: connected[0].id
|
|
@@ -73,16 +83,14 @@ function disambiguateConnections(connectionReferences, allConnectionsByApiId) {
|
|
|
73
83
|
* "abc12345def6789012345678901234ab" (bare UUID, no dashes)
|
|
74
84
|
* "abc12345-def6-7890-1234-5678901234ab" (UUID with dashes)
|
|
75
85
|
*
|
|
76
|
-
* Returns the
|
|
77
|
-
*
|
|
86
|
+
* Returns the terminal resource name exactly as supplied by Power Platform.
|
|
87
|
+
* Some connectors require dashed GUIDs while others use compact IDs.
|
|
78
88
|
*/
|
|
79
89
|
function extractConnectionId(rawName) {
|
|
80
|
-
// Match a standard UUID (with or without dashes) at the end of the string.
|
|
81
|
-
const match = rawName.match(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i);
|
|
82
|
-
if (match) {
|
|
83
|
-
return match[1].replace(/-/g, '');
|
|
84
|
-
}
|
|
85
|
-
// Some connections use a 32-char hex ID without dashes, after a slash.
|
|
86
90
|
const slashIdx = rawName.lastIndexOf('/');
|
|
87
91
|
return slashIdx >= 0 ? rawName.slice(slashIdx + 1) : rawName;
|
|
92
|
+
}
|
|
93
|
+
function connectionIdsEqual(left, right) {
|
|
94
|
+
const normalize = value => extractConnectionId(value).replace(/-/g, '').toLowerCase();
|
|
95
|
+
return normalize(left) === normalize(right);
|
|
88
96
|
}
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.applyAclGrants = applyAclGrants;
|
|
7
|
+
exports.resolveViaStaticRefs = resolveViaStaticRefs;
|
|
8
|
+
var _PowerPlatformConnectionApi = require("./PowerPlatformConnectionApi.js");
|
|
9
|
+
var _connectionState = require("./connectionState.js");
|
|
10
|
+
var _PowerPlatformEnvironmentApi = require("../platform/PowerPlatformEnvironmentApi.js");
|
|
11
|
+
var _DataverseGlobalDiscoveryApi = require("../platform/DataverseGlobalDiscoveryApi.js");
|
|
12
|
+
var _disambiguate = require("../connections/disambiguate.js");
|
|
13
|
+
var _types = require("./types.js");
|
|
14
|
+
var _index = require("../telemetry/index.js");
|
|
15
|
+
/*!
|
|
16
|
+
* power-apps-native-host — resolveConnections
|
|
17
|
+
*
|
|
18
|
+
* Pure async helpers for the two connection-resolution strategies used by
|
|
19
|
+
* useConnectionRefs:
|
|
20
|
+
*
|
|
21
|
+
* • resolveViaStaticRefs — per-connector resolution using the static
|
|
22
|
+
* connectionReferences map from power.config.json. Used only by Dev Player
|
|
23
|
+
* for unpublished local apps. Handles pre-disambiguation ACL grants so
|
|
24
|
+
* inaccessible connections are excluded before the user is asked to pick.
|
|
25
|
+
*
|
|
26
|
+
* • applyAclGrants — post-resolution user consent used by the fast path
|
|
27
|
+
* (production APKs via fetchAppLaunchData).
|
|
28
|
+
*
|
|
29
|
+
* Both helpers return / consume the shared AppLaunchResult shape so the
|
|
30
|
+
* hook orchestrator can handle them uniformly.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
const API_VERSION = '1';
|
|
34
|
+
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// ConnectorSchema
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Connector schema entry generated by `generate-connector-schemas`.
|
|
41
|
+
* Keyed by data source name in the `schemaMap` passed to `useConnectionRefs`.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// resolveViaStaticRefs
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Resolve connections from the static `connectionReferences` map.
|
|
50
|
+
*
|
|
51
|
+
* Steps:
|
|
52
|
+
* 1. Build an apiId → schema lookup.
|
|
53
|
+
* 2. In parallel: fetch the Dataverse instanceUrl and all connection instances
|
|
54
|
+
* for every connector.
|
|
55
|
+
* 3. If `isvClientId` is set, run pre-disambiguation user consent — connections
|
|
56
|
+
* where the grant fails are marked `NeedsAclGrant` and excluded from
|
|
57
|
+
* resolution, prompting the user to create a new connection.
|
|
58
|
+
* 4. Disambiguate: resolve single matches, invoke callbacks for ambiguous /
|
|
59
|
+
* missing cases, perform one-shot retries when `onMissingConnection` returns
|
|
60
|
+
* `true`.
|
|
61
|
+
* 5. Build and return an `AppLaunchResult`-shaped object.
|
|
62
|
+
*/
|
|
63
|
+
async function resolveViaStaticRefs({
|
|
64
|
+
connectionReferences,
|
|
65
|
+
hasDataverse,
|
|
66
|
+
schemaMap,
|
|
67
|
+
environmentId,
|
|
68
|
+
region,
|
|
69
|
+
acquireToken,
|
|
70
|
+
token,
|
|
71
|
+
isvClientId,
|
|
72
|
+
onDisambiguationNeeded,
|
|
73
|
+
onMissingConnection
|
|
74
|
+
}) {
|
|
75
|
+
const directConnectionReferences = Object.fromEntries(Object.entries(connectionReferences).filter(([, reference]) => !(0, _types.isCompositeConnectionApiId)(reference.id)));
|
|
76
|
+
const flowConnectionReferences = Object.fromEntries(Object.entries(connectionReferences).filter(([, reference]) => (0, _types.getConnectorId)(reference.id) === 'shared_logicflows'));
|
|
77
|
+
// Build apiId → schema lookup from the static refs.
|
|
78
|
+
const schemaByApiId = Object.fromEntries(Object.values(directConnectionReferences).map(ref => {
|
|
79
|
+
const schema = ref.dataSources.map(ds => schemaMap[ds]).find(Boolean);
|
|
80
|
+
return schema ? [(0, _types.normalizeApiId)(ref.id), schema] : null;
|
|
81
|
+
}).filter(entry => entry !== null));
|
|
82
|
+
await Promise.all(Object.values({
|
|
83
|
+
...directConnectionReferences,
|
|
84
|
+
...flowConnectionReferences
|
|
85
|
+
}).map(async reference => {
|
|
86
|
+
const apiId = (0, _types.normalizeApiId)(reference.id);
|
|
87
|
+
if (schemaByApiId[apiId]) return;
|
|
88
|
+
const metadata = await (0, _PowerPlatformConnectionApi.fetchConnectorMetadata)((0, _types.getConnectorId)(reference.id), environmentId, region, token);
|
|
89
|
+
if (!metadata?.primaryRuntimeUrl) return;
|
|
90
|
+
schemaByApiId[apiId] = {
|
|
91
|
+
name: metadata.name,
|
|
92
|
+
properties: {
|
|
93
|
+
primaryRuntimeUrl: metadata.primaryRuntimeUrl,
|
|
94
|
+
scopes: metadata.scopes
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}));
|
|
98
|
+
const base = (0, _PowerPlatformEnvironmentApi.buildEnvironmentEndpoint)(environmentId, region);
|
|
99
|
+
const headers = {
|
|
100
|
+
Authorization: `Bearer ${token}`
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// Fetch the Dataverse instanceUrl only when the app uses Dataverse.
|
|
104
|
+
const [instanceUrl, ...connResults] = await Promise.all([hasDataverse ? (0, _DataverseGlobalDiscoveryApi.fetchInstanceUrl)(environmentId, region, acquireToken, token) : Promise.resolve(undefined), ...Object.entries(directConnectionReferences).map(async ([localRefId, ref]) => {
|
|
105
|
+
const apiId = (0, _types.normalizeApiId)(ref.id);
|
|
106
|
+
const schema = schemaByApiId[apiId];
|
|
107
|
+
if (!schema) {
|
|
108
|
+
return {
|
|
109
|
+
localRefId,
|
|
110
|
+
apiId,
|
|
111
|
+
dataSources: ref.dataSources,
|
|
112
|
+
instances: []
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
const url = `${base}/connectivity/connectors/${schema.name}/connections` + `?api-version=${API_VERSION}&$filter=environment eq '${environmentId}'`;
|
|
116
|
+
try {
|
|
117
|
+
const res = await _index.platformHttpClient.request(url, {
|
|
118
|
+
headers
|
|
119
|
+
});
|
|
120
|
+
if (!res.ok) {
|
|
121
|
+
return {
|
|
122
|
+
localRefId,
|
|
123
|
+
apiId,
|
|
124
|
+
dataSources: ref.dataSources,
|
|
125
|
+
instances: []
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
const body = await res.json();
|
|
129
|
+
const instances = (body.value ?? []).map(c => {
|
|
130
|
+
const connected = c.properties?.statuses?.[0]?.status === 'Connected';
|
|
131
|
+
return {
|
|
132
|
+
id: (0, _disambiguate.extractConnectionId)(c.name),
|
|
133
|
+
name: c.name,
|
|
134
|
+
apiId,
|
|
135
|
+
ownerId: c.properties?.createdBy?.id,
|
|
136
|
+
status: connected ? _types.ConnectionStatus.Connected : _types.ConnectionStatus.NotConnected,
|
|
137
|
+
displayName: c.properties?.displayName,
|
|
138
|
+
isAuthenticated: connected
|
|
139
|
+
};
|
|
140
|
+
});
|
|
141
|
+
return {
|
|
142
|
+
localRefId,
|
|
143
|
+
apiId,
|
|
144
|
+
dataSources: ref.dataSources,
|
|
145
|
+
instances
|
|
146
|
+
};
|
|
147
|
+
} catch {
|
|
148
|
+
return {
|
|
149
|
+
localRefId,
|
|
150
|
+
apiId,
|
|
151
|
+
dataSources: ref.dataSources,
|
|
152
|
+
instances: []
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
})]);
|
|
156
|
+
|
|
157
|
+
// Pre-disambiguation delegated consent grants (ISV apps only).
|
|
158
|
+
//
|
|
159
|
+
// Connections where the grant fails are marked NeedsAclGrant so
|
|
160
|
+
// disambiguation treats them as unavailable and surfaces the
|
|
161
|
+
// "missing connection" flow, steering the user to create a new one. Fixed
|
|
162
|
+
if (isvClientId) {
|
|
163
|
+
const consentToken = await acquireToken([(0, _PowerPlatformEnvironmentApi.connectionUserConsentScope)(region)]);
|
|
164
|
+
if (consentToken) {
|
|
165
|
+
const attemptedGrants = new Set();
|
|
166
|
+
await Promise.all(connResults.map(async r => {
|
|
167
|
+
const reference = directConnectionReferences[r.localRefId];
|
|
168
|
+
if (!reference || !(0, _connectionState.shouldGrantConnectionUserConsent)(reference, true)) return;
|
|
169
|
+
const connId = schemaByApiId[r.apiId]?.name;
|
|
170
|
+
if (!connId) return;
|
|
171
|
+
await Promise.all(r.instances.map(async inst => {
|
|
172
|
+
if (inst.status !== _types.ConnectionStatus.Connected) return;
|
|
173
|
+
if (reference?.sharedConnectionId && !(0, _disambiguate.connectionIdsEqual)(inst.id, reference.sharedConnectionId)) return;
|
|
174
|
+
const grantKey = `${connId.toLowerCase()}/${inst.id.toLowerCase()}`;
|
|
175
|
+
if (attemptedGrants.has(grantKey)) return;
|
|
176
|
+
attemptedGrants.add(grantKey);
|
|
177
|
+
const ok = await (0, _PowerPlatformConnectionApi.grantConnectionUserConsent)(connId, inst.id, environmentId, region, consentToken);
|
|
178
|
+
if (!ok) inst.status = _types.ConnectionStatus.NeedsAclGrant;
|
|
179
|
+
}));
|
|
180
|
+
}));
|
|
181
|
+
} else {
|
|
182
|
+
for (const result of connResults) {
|
|
183
|
+
for (const instance of result.instances) {
|
|
184
|
+
if (instance.status === _types.ConnectionStatus.Connected) {
|
|
185
|
+
instance.status = _types.ConnectionStatus.NeedsAclGrant;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Disambiguation.
|
|
193
|
+
const allConnectionsByApiId = {};
|
|
194
|
+
for (const r of connResults) {
|
|
195
|
+
allConnectionsByApiId[r.apiId] = r.instances;
|
|
196
|
+
}
|
|
197
|
+
const disambiguationResults = (0, _disambiguate.disambiguateConnections)(directConnectionReferences, allConnectionsByApiId);
|
|
198
|
+
const resolvedIds = {};
|
|
199
|
+
const unresolvedApiIds = [];
|
|
200
|
+
for (const dr of disambiguationResults) {
|
|
201
|
+
if (dr.result.kind === 'resolved') {
|
|
202
|
+
resolvedIds[dr.localRefId] = dr.result.connectionId;
|
|
203
|
+
} else if (dr.result.kind === 'ambiguous') {
|
|
204
|
+
const picked = onDisambiguationNeeded ? await onDisambiguationNeeded({
|
|
205
|
+
localRefId: dr.localRefId,
|
|
206
|
+
apiId: dr.apiId,
|
|
207
|
+
candidates: dr.result.candidates
|
|
208
|
+
}) : null;
|
|
209
|
+
if (picked) {
|
|
210
|
+
resolvedIds[dr.localRefId] = picked;
|
|
211
|
+
} else {
|
|
212
|
+
resolvedIds[dr.localRefId] = '';
|
|
213
|
+
unresolvedApiIds.push((0, _types.normalizeApiId)(dr.apiId));
|
|
214
|
+
}
|
|
215
|
+
} else {
|
|
216
|
+
// kind === 'missing'
|
|
217
|
+
let retried = false;
|
|
218
|
+
if (onMissingConnection) {
|
|
219
|
+
retried = await onMissingConnection({
|
|
220
|
+
localRefId: dr.localRefId,
|
|
221
|
+
apiId: dr.apiId
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
if (retried) {
|
|
225
|
+
// One-shot retry for this specific connector.
|
|
226
|
+
const schema = schemaByApiId[dr.apiId];
|
|
227
|
+
if (schema) {
|
|
228
|
+
try {
|
|
229
|
+
const url = `${base}/connectivity/connectors/${schema.name}/connections` + `?api-version=${API_VERSION}&$filter=environment eq '${environmentId}'`;
|
|
230
|
+
const res = await _index.platformHttpClient.request(url, {
|
|
231
|
+
headers
|
|
232
|
+
});
|
|
233
|
+
if (res.ok) {
|
|
234
|
+
const body = await res.json();
|
|
235
|
+
const retryInstances = (body.value ?? []).map(c => {
|
|
236
|
+
const connected = c.properties?.statuses?.[0]?.status === 'Connected';
|
|
237
|
+
return {
|
|
238
|
+
id: (0, _disambiguate.extractConnectionId)(c.name),
|
|
239
|
+
name: c.name,
|
|
240
|
+
apiId: dr.apiId,
|
|
241
|
+
status: connected ? _types.ConnectionStatus.Connected : _types.ConnectionStatus.NotConnected,
|
|
242
|
+
isAuthenticated: connected
|
|
243
|
+
};
|
|
244
|
+
});
|
|
245
|
+
const connected = retryInstances.find(i => i.status === _types.ConnectionStatus.Connected);
|
|
246
|
+
if (connected) {
|
|
247
|
+
resolvedIds[dr.localRefId] = connected.id;
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
} catch {
|
|
252
|
+
// Fall through to missing.
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
resolvedIds[dr.localRefId] = '';
|
|
257
|
+
unresolvedApiIds.push((0, _types.normalizeApiId)(dr.apiId));
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
const dataverseOrgUrl = (instanceUrl ?? '').replace(/\/$/, '');
|
|
261
|
+
|
|
262
|
+
// Build the final AppConnectionReference map.
|
|
263
|
+
const connectionRefs = {};
|
|
264
|
+
for (const [localRefId, ref] of Object.entries(directConnectionReferences)) {
|
|
265
|
+
const normalizedApiId = (0, _types.normalizeApiId)(ref.id);
|
|
266
|
+
const schema = schemaByApiId[normalizedApiId];
|
|
267
|
+
const connectionName = resolvedIds[localRefId] ?? '';
|
|
268
|
+
const isDataverse = ref.id.includes('commondataserviceforapps');
|
|
269
|
+
const entry = {
|
|
270
|
+
apiId: normalizedApiId,
|
|
271
|
+
runtimeUrl: schema?.properties.primaryRuntimeUrl ?? '',
|
|
272
|
+
connectionName,
|
|
273
|
+
datasetName: isDataverse ? dataverseOrgUrl : (0, _types.getConnectionDatasetName)(ref)
|
|
274
|
+
};
|
|
275
|
+
for (const ds of ref.dataSources) {
|
|
276
|
+
connectionRefs[ds] = entry;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
for (const ref of Object.values(flowConnectionReferences)) {
|
|
280
|
+
const normalizedApiId = (0, _types.normalizeApiId)(ref.id);
|
|
281
|
+
const schema = schemaByApiId[normalizedApiId];
|
|
282
|
+
const entry = {
|
|
283
|
+
apiId: normalizedApiId,
|
|
284
|
+
runtimeUrl: schema?.properties.primaryRuntimeUrl ?? '',
|
|
285
|
+
connectionName: (0, _types.getFlowConnectionName)(ref),
|
|
286
|
+
datasetName: ''
|
|
287
|
+
};
|
|
288
|
+
for (const dataSource of (0, _types.getFlowDataSourceNames)(ref)) {
|
|
289
|
+
connectionRefs[dataSource] = entry;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
const scopesByApiId = Object.fromEntries(Object.entries(schemaByApiId).map(([apiId, schema]) => [apiId, {
|
|
293
|
+
will: schema.properties.scopes?.will ?? [],
|
|
294
|
+
wont: schema.properties.scopes?.wont ?? []
|
|
295
|
+
}]));
|
|
296
|
+
const requirements = (0, _types.buildConnectionRequirements)(connectionReferences, scopesByApiId);
|
|
297
|
+
const requirementsByLocalRefId = Object.fromEntries(requirements.map(requirement => [requirement.localRefId, requirement]));
|
|
298
|
+
const missingConnectionReferences = Object.entries(resolvedIds).filter(([, connectionId]) => !connectionId).map(([localRefId]) => requirementsByLocalRefId[localRefId]).filter(requirement => Boolean(requirement));
|
|
299
|
+
const connectionCandidates = Object.fromEntries(connResults.map(result => [result.localRefId, result.instances]));
|
|
300
|
+
return {
|
|
301
|
+
connectionRefs,
|
|
302
|
+
dataverseOrgUrl,
|
|
303
|
+
databaseReferences: {},
|
|
304
|
+
missingConnectors: [...new Set(unresolvedApiIds)],
|
|
305
|
+
missingConnectionReferences,
|
|
306
|
+
connectionRequirements: Object.values(requirementsByLocalRefId),
|
|
307
|
+
connectionCandidates,
|
|
308
|
+
resourceConsents: [],
|
|
309
|
+
configurationErrors: (0, _types.findConnectionDataSourceConflicts)(requirements).map(dataSource => `Data source "${dataSource}" is assigned to multiple connection references.`),
|
|
310
|
+
registeredConnectionUsages: [],
|
|
311
|
+
requiresConnectionConsent: requirements.length > 0
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// ---------------------------------------------------------------------------
|
|
316
|
+
// applyAclGrants
|
|
317
|
+
// ---------------------------------------------------------------------------
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Run idempotent post-resolution delegated consent grants for the calling app.
|
|
321
|
+
*
|
|
322
|
+
* Used by the fast path (production APKs via `fetchAppLaunchData`) where the
|
|
323
|
+
* launch API has already resolved the user's connections. Grants are applied
|
|
324
|
+
* only to user-selected connections; fixed shared connections are already
|
|
325
|
+
* authorized by their owner and must not receive delegated user consent.
|
|
326
|
+
*
|
|
327
|
+
* For the slow path the grants run before disambiguation inside
|
|
328
|
+
* `resolveViaStaticRefs`, where they can still influence which connections
|
|
329
|
+
* are considered available.
|
|
330
|
+
*
|
|
331
|
+
* Returns the connection names whose ACL grants failed so callers can fail closed.
|
|
332
|
+
*/
|
|
333
|
+
async function applyAclGrants(connectionRefs, connectionRequirements, environmentId, region, acquireToken) {
|
|
334
|
+
const connectionNames = (0, _connectionState.getConnectionNamesRequiringAclGrant)(connectionRefs, connectionRequirements);
|
|
335
|
+
if (connectionNames.length === 0) return new Set();
|
|
336
|
+
const connectionNamesSet = new Set(connectionNames);
|
|
337
|
+
const consentToken = await acquireToken([(0, _PowerPlatformEnvironmentApi.connectionUserConsentScope)(region)]);
|
|
338
|
+
if (!consentToken) return new Set(connectionNames);
|
|
339
|
+
const seen = new Set();
|
|
340
|
+
const failed = new Set();
|
|
341
|
+
await Promise.all(Object.values(connectionRefs).map(async ref => {
|
|
342
|
+
if (!ref.connectionName || !connectionNamesSet.has(ref.connectionName) || seen.has(ref.connectionName)) return;
|
|
343
|
+
seen.add(ref.connectionName);
|
|
344
|
+
const granted = await (0, _PowerPlatformConnectionApi.grantConnectionUserConsent)(ref.apiId, ref.connectionName, environmentId, region, consentToken);
|
|
345
|
+
if (!granted) failed.add(ref.connectionName);
|
|
346
|
+
}));
|
|
347
|
+
return failed;
|
|
348
|
+
}
|