Skip to content

[Feature Request] Injectable auth/connection provider for the data executor (headless / CI integration testing) #398

Description

@This-Is-NPC

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 to DataverseDataOperationExecutor, whose environment URL and bearer token come from the host handshake (OperationExecutorloadConnections / 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/data

setDataOperationExecutor(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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions