You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A Code App's generated data services can only reach Dataverse through the Power Apps host (browser player or pac code run). The services route through the runtime down to DataverseDataOperationExecutor, whose environment URL and bearer token come from the host handshake (OperationExecutor → loadConnections / executePluginAsync). Outside the host there is no supported way to supply them, so the real service code cannot run — which means there is no way to run the app's real generated services against Dataverse in a headless runner (Vitest/Node, CI).
The executor seam already exists — DefaultDataOperationOrchestrator._getExecutor() checks setDataOperationExecutor(...) first, before any host lookup — but that setter is only exposed via the internal subpath @microsoft/power-apps/internal/data. The public @microsoft/power-apps/data/executors subpath already exists and exports createMockDataExecutor, MockDataStore, and the IDataOperationExecutor interface — so the executor contract is already public; only the setter and a real Dataverse factory are missing. What is missing is a supported, host-independent way to authenticate and target an environment. (Verified against @microsoft/power-apps@1.2.5.)
The available testing options each fall short, so automated integration testing of a Code App's real data layer is effectively impossible today:
Hand-mocked services — never exercise the real service/query code path.
createMockDataExecutor — supported, but in-memory; ignores query options, so OData translation, @odata.bind lookups, and @odata.nextLink paging are never tested.
Playwright against the player — real, but requires a browser and an interactive sign-in; not viable for fast headless CI.
Proposed improvement
Expose a supported factory that builds the existing DataverseDataOperationExecutor with an injected token/URL provider, and promote the setter to a supported subpath:
import{createDataverseExecutor}from'@microsoft/power-apps/data/executors';import{setDataOperationExecutor}from'@microsoft/power-apps/data';// promote from internal/datasetDataOperationExecutor(createDataverseExecutor({environmentUrl: 'https://<org>.crm.dynamics.com',getAccessToken: async()=>getToken('https://<org>.crm.dynamics.com/.default'),}));// the app's REAL generated services now run headlessly:// await ProjectService.getAll({ filter: "...", select: [...] });
getAccessToken: () => Promise<string> keeps credential acquisition with the caller (service principal, device code, cached token) — the SDK stays out of the auth flow.
Reusing the SDK's own Dataverse executor guarantees query/lookup/paging semantics match the host exactly.
Additive change: with no override set, behavior is unchanged.
A minimal version could just promote setDataOperationExecutor to a supported subpath — IDataOperationExecutor is already exported from @microsoft/power-apps/data/executors, so consumers can already implement their own executor; only the setter is gated behind internal/data.
Proposed flow (headless, no host):
sequenceDiagram
participant Test as Test / CI (Node)
participant Factory as createDataverseExecutor
participant Svc as Generated service
participant Orch as DefaultDataOperationOrchestrator
participant Exec as Injected Dataverse executor
participant Auth as getAccessToken (caller)
participant DV as Dataverse Web API
Test->>Factory: createDataverseExecutor({ environmentUrl, getAccessToken })
Test->>Orch: setDataOperationExecutor(executor)
Test->>Svc: ProjectService.getAll({ filter, select })
Svc->>Orch: retrieveMultipleRecordsAsync(table, options)
Orch->>Orch: _getExecutor() → returns override (no host lookup)
Orch->>Exec: retrieveMultipleRecordsAsync(table, options)
Exec->>Auth: getAccessToken()
Auth-->>Exec: bearer token
Exec->>DV: GET /api/data/v9.2/<table>?$filter&$select
DV-->>Exec: records
Exec-->>Svc: IOperationResult<T>
Svc-->>Test: data
Loading
Related
Not a duplicate of existing items. Related but distinct: device-code auth request (#311 / discussion #315) and service-principal pac code push failures (#394, #377, #305, #304, #244) — those concern CLI/push auth, not the runtime data executor.
Environment information
Package: @microsoft/power-apps (verified against 1.2.5)
The limitation
A Code App's generated data services can only reach Dataverse through the Power Apps host (browser player or
pac code run). The services route through the runtime down toDataverseDataOperationExecutor, whose environment URL and bearer token come from the host handshake (OperationExecutor→loadConnections/executePluginAsync). Outside the host there is no supported way to supply them, so the real service code cannot run — which means there is no way to run the app's real generated services against Dataverse in a headless runner (Vitest/Node, CI).The executor seam already exists —
DefaultDataOperationOrchestrator._getExecutor()checkssetDataOperationExecutor(...)first, before any host lookup — but that setter is only exposed via the internal subpath@microsoft/power-apps/internal/data. The public@microsoft/power-apps/data/executorssubpath already exists and exportscreateMockDataExecutor,MockDataStore, and theIDataOperationExecutorinterface — so the executor contract is already public; only the setter and a real Dataverse factory are missing. What is missing is a supported, host-independent way to authenticate and target an environment. (Verified against@microsoft/power-apps@1.2.5.)The available testing options each fall short, so automated integration testing of a Code App's real data layer is effectively impossible today:
createMockDataExecutor— supported, but in-memory; ignores queryoptions, so OData translation,@odata.bindlookups, and@odata.nextLinkpaging are never tested.Proposed improvement
Expose a supported factory that builds the existing
DataverseDataOperationExecutorwith an injected token/URL provider, and promote the setter to a supported subpath:getAccessToken: () => Promise<string>keeps credential acquisition with the caller (service principal, device code, cached token) — the SDK stays out of the auth flow.setDataOperationExecutorto a supported subpath —IDataOperationExecutoris already exported from@microsoft/power-apps/data/executors, so consumers can already implement their own executor; only the setter is gated behindinternal/data.Proposed flow (headless, no host):
sequenceDiagram participant Test as Test / CI (Node) participant Factory as createDataverseExecutor participant Svc as Generated service participant Orch as DefaultDataOperationOrchestrator participant Exec as Injected Dataverse executor participant Auth as getAccessToken (caller) participant DV as Dataverse Web API Test->>Factory: createDataverseExecutor({ environmentUrl, getAccessToken }) Test->>Orch: setDataOperationExecutor(executor) Test->>Svc: ProjectService.getAll({ filter, select }) Svc->>Orch: retrieveMultipleRecordsAsync(table, options) Orch->>Orch: _getExecutor() → returns override (no host lookup) Orch->>Exec: retrieveMultipleRecordsAsync(table, options) Exec->>Auth: getAccessToken() Auth-->>Exec: bearer token Exec->>DV: GET /api/data/v9.2/<table>?$filter&$select DV-->>Exec: records Exec-->>Svc: IOperationResult<T> Svc-->>Test: dataRelated
Not a duplicate of existing items. Related but distinct: device-code auth request (#311 / discussion #315) and service-principal
pac code pushfailures (#394, #377, #305, #304, #244) — those concern CLI/push auth, not the runtime data executor.Environment information
@microsoft/power-apps(verified against1.2.5)