@mohsinonxrm/pptb-dataverse-request-studio
v1.1.0
Published
Compose, preview, and execute Microsoft Dataverse Web API requests — a metadata-driven studio for OData queries, record writes, associations, action/function/workflow invocation, and file/image/attachment operations. Embeds in Power Platform ToolBox.
Maintainers
Readme
Dataverse Request Studio for Power Platform ToolBox
A metadata-driven studio for composing, previewing, and executing Microsoft Dataverse Web API requests — covering OData reads, record writes, table associations, bound and unbound action/function invocation, on-demand workflows, and file / image / attachment operations.
Built for Power Platform ToolBox with React 18, Fluent UI v9, TypeScript, and live Dataverse metadata integration.
✨ Features
DRS ships with 17 request modes organized into 5 functional groups. Every mode is metadata-driven, generates the correct OData / Web API shape, and emits ready-to-copy code in five languages (xrm.WebApi single + batch, fetch, curl, PowerShell).
📖 Read group
- Retrieve Multiple — full OData query composer with
$select,$filter,$orderby,$expand,$top,$skip,$count,$apply(groupby / aggregate), and a virtualized results grid with infinite scroll via@odata.nextLink. - Retrieve Single — by primary key OR by alternate key (composite alternate keys supported).
- Retrieve NextLink — paste a
@odata.nextLinkURL to continue paging any query. - Predefined Query — execute saved-query (
?savedQuery=<id>) and user-query (?userQuery=<id>) URLs against an entity set; layout-aware results grid that matches the source view's column order.
✏️ Write group
- Create Record —
POST /<set>with metadata-driven field-set editor. Required-field guard with redreqbadges,Prefer: return=representationtoggle, optional$selectreturn list,MSCRM.*bypass-header family. - Update Record —
PATCH /<set>(<id>)multi-field ORPUT /<set>(<id>)/<col>single-column. Diff view (before → after) with per-row drill-down to switch to PUT mode. - Upsert Record — full upsert / create-only / update-only / etag-guarded variants. By GUID OR alternate key.
- Delete Record —
DELETE /<set>(<id>)with a typed-confirmation safety affordance + cascade-impact preview. Single-property delete viaDELETE /<set>(<id>)/<col>also supported. - Merge Records —
POST /Mergeforaccount/contact/incidentwith target + subordinate record cards, field-override matrix, and PerformParentingChecks / SuppressDuplicateDetection settings.
🔗 Relate group
- Associate — links related records via
POST /<set>(<id>)/<nav>/$reffor collection-valued navs, orPATCH @odata.bindfor single-valued. Cardinality detected from metadata; verb auto-flips with the picker. Polymorphic lookups (Customer / Owner /regardingobjectid) fully supported with target-disambiguated nav properties. - Disassociate —
DELETE /$reffor collection-valued,PATCH @odata.bind: nullfor single-valued. Same polymorphism story.
⚡ Execute group
- Execute Action — OOB / Custom API / Custom Action invocation via
bound
POST /<set>(<id>)/Microsoft.Dynamics.CRM.<Action>OR unboundPOST /<Action>. CSDL-driven parameter form with type-aware controls: required-first UX, Form ↔ JSON toggle, EntityReference / EntityCollection / Edm primitives / Enum / Status-code OptionSet enrichment. - Execute Function — OData function calls with parameter aliasing
(
?@p1=...) vs inline literal toggle. CSDL-driven param form with the same type-aware controls as actions. - Execute Workflow — runs on-demand workflows via
POST /workflows(<id>)/Microsoft.Dynamics.CRM.ExecuteWorkflowwith the bound record id. Live picker over activated, on-demand process workflows (statecode eq 1 and type eq 1 and ondemand eq true and category eq 0).
💾 Binary group
- Manage File Data — File-column upload / download / delete with three
upload paths (single-request PATCH, chunked PATCH with session token,
InitializeFileBlocksUpload/UploadBlock/CommitFileBlocksUploadmessages), four download paths (single GET/$value, ranged GET withRange, message-based block download,GetFileSasUrlfunction), and theDeleteFilemessage + DELETE-on-property alternatives. - Manage Image Data — Image columns with thumbnail vs full-size
download (
?size=full),CanStoreFullImageawareness, and pointers to the file-column path for full-size uploads. - Manage Attachment / Note —
activitymimeattachment.bodyandannotation.documentbody. Inline base 64 ORInitialize{Attachment|Annotation}BlocksUpload/Commit{Attachment|Annotation}BlocksUploadmessages. Single-request GET/$valueORInitialize{...}BlocksDownload/DownloadBlockfor download.
🔍 Cross-cutting capabilities
- Live metadata — Tables, columns, relationships, alternate keys, action
/ function CSDL all fetched lazily from the host's
dataverseAPI. Per-org TTL cache with in-flight promise dedup. Type-specific column properties (Targets, OptionSet, MaxLength, MinValue/MaxValue, Format) loaded on-demand by the editor that needs them. - Antipattern advisories — Surfaces real-time guidance per the
Microsoft query-antipatterns doc:
FilteringOnCalculatedColumns,LargeAmountOfLogicalAttributes, large-textcontains, leading-wildcard rewrites — all flagged inline AND aggregated into an Advisory drawer in the URL bar. - Polymorphic lookup support — Customer, Owner, and multi-target lookups
(
regardingobjectid) emit the correct target-disambiguated<navName>@odata.bindbinding shape automatically. - Code generators — Every mode emits the same request as
Xrm.WebApi.online.execute(single +executeMultiplebatch),fetch,curl, and PowerShellInvoke-RestMethodsnippets. Multi-request pipelines (chunked uploads, message-based binary ops) render as full ordered sequences. - Saved-request library — Per-org localStorage persistence with
auto-suggested names (
account · 3 sel · 2 flt · 2026-05-23), rename / delete / overwrite, dirty-state detection, and cross-tab sync. - PPTB host integration —
isEmbedded()-aware UI: theme sync, environment-aware URLs, header pass-through, host-stripped header advisories. Header / theme controls hide when the host owns them.
🖼️ Interface overview
┌─────────────────────────────────────────────────────────────────────────┐
│ [Request Type ▼] [Group · Method] [⚙ Settings] [Theme] │
├──────────────────────────┬──────────────────────────────────────────────┤
│ │ [Builder] [Code] [Results] │
│ Sidebar ├──────────────────────────────────────────────┤
│ │ │
│ 📋 Target │ Active editor pane │
│ account │ │
│ 📊 $select 3 sel │ ┌────────────────────────────────────────┐ │
│ 🔍 $filter 2 flt │ │ (clause editor — varies per mode) │ │
│ 🔁 $orderby 1 sort│ │ │ │
│ 🔗 $expand 1 exp │ │ • Field-set editor (writes) │ │
│ 📃 $top │ │ • Filter tree (reads) │ │
│ ⚙ Prefer │ │ • Action param form (executes) │ │
│ 📥 Headers 1 act │ │ • Binary pipeline (file/image) │ │
│ │ └────────────────────────────────────────┘ │
│ ⏱ Recent runs │ │
│ 200 · 142ms · 32B │ │
│ 200 · 89ms │ │
│ │ │
├──────────────────────────┴──────────────────────────────────────────────┤
│ GET /accounts?$select=name,… 0.4 KB ⚠ 2 advisories │
│ [▶ Execute] │
└─────────────────────────────────────────────────────────────────────────┘🚀 Getting started
Prerequisites
- Power Platform ToolBox (for embedded use)
- A connection to a Dataverse environment
- For local development: Node.js ≥ 20.19 and npm
Installation
Dataverse Request Studio is available as a tool in Power Platform ToolBox. Install it from the tool gallery or load it as a custom tool.
🛠️ Development
Setup
git clone https://github.com/mohsinonxrm/pptb-mxrm-dataverse-request-studio.git
cd pptb-mxrm-dataverse-request-studio
npm installCommon scripts
# Vite dev server (standalone, no PPTB host)
npm run dev
# TypeScript-only check (fast)
npm run typecheck
# Production build
npm run build
# Preview the production build locally
npm run preview
# Validate manifest against PPTB registry rules
npm run validate
# Pre-publish: validate → build → shrinkwrap
npm run finalize-packageStandalone vs PPTB-embedded
DRS runs in two modes:
- Standalone (
npm run dev) — opens in a regular browser tab. Useful for UI iteration. Execute will return501 Not Implementedfor any operation because the host bridge isn't present; you can still build requests, preview URLs, and use the Code tab. - Embedded in PPTB — loads inside the PPTB iframe. The host injects
window.dataverseAPI(typed) andwindow.toolboxAPI. Execute runs against the active Dataverse connection.
See docs/CONTRIBUTING.md for branch conventions, commit style, code style, and PR guidance.
📦 Tech stack
| Technology | Version | Purpose | |---|---|---| | React | 18.3 | UI framework | | TypeScript | 5.9 | Strict mode, discriminated unions | | Vite | 7 | Build tooling + HMR | | Fluent UI v9 | 9.73 | Microsoft design system (Combobox, DataGrid, Tree, Drawer, Tabs, etc.) | | Monaco Editor | 0.54 | JSON / code editor for body authoring and Code tab | | @fluentui-contrib/react-data-grid-react-window | 1.4 | Virtualized results grid | | @dnd-kit | 6.3 | Drag-reordering for filter / orderby / select clauses | | @pptb/types | 1.2.1 | Power Platform ToolBox host API types |
📁 Project layout
src/
App.tsx — root, ThemedApp, Frame, mode router
main.tsx — Vite entry point
modes/ — one file per request mode (17 modes)
RetrieveMultipleMode.tsx
RetrieveSingleMode.tsx
RetrieveNextLinkMode.tsx
PredefinedQueryMode.tsx
CreateMode.tsx / UpdateMode.tsx / UpsertMode.tsx / DeleteMode.tsx
MergeMode.tsx
AssociateMode.tsx / DisassociateMode.tsx
ExecuteActionMode.tsx / ExecuteFunctionMode.tsx / ExecuteWorkflowMode.tsx
ManageFileMode.tsx / ManageImageMode.tsx / ManageAttachmentMode.tsx
editors/ — clause editors shared across modes
filter/ — FilterEditor + value inputs + operator catalog
FieldSetEditor.tsx — metadata-driven write-side form
SelectEditor.tsx, OrderbyEditor.tsx, ExpandEditor.tsx, ApplyEditor.tsx
ActionParamForm.tsx — CSDL-driven action / function params
BinarySourceCard.tsx, BinaryPipelineCard.tsx, BinaryColumnPickers.tsx
HeadersEditor.tsx, BypassEditor.tsx
ActionPicker.tsx, RecordPicker.tsx, EntityListPicker.tsx
...
engine/ — URL / body construction + code generation
urlBuilder.ts — central URL builder for every mode
executeBuilders.ts — action / function / workflow body builders
binaryBuilders.ts — file / image / attachment pipelines
codeGenerators.ts — xrm, xrm-batch, fetch, curl, PowerShell
runtime.ts — single dispatch point for execute
dataverseExecutor.ts — live executor over the host's dataverseAPI
odataParser.ts — round-trips OData URL strings ↔ state
host/ — PPTB bridge + metadata loaders
pptbBridge.ts — typed wrapper over window.dataverseAPI
metadataProvider.ts — entity / attribute / relationship loaders
csdlProvider.ts — $metadata XML parsing + action / function index
cache.ts — singleton TTL cache with in-flight dedup
useLiveMetadata.ts, useColumnDetail.ts, useScopedEntities.ts, …
primitives/ — small reusable UI bits
Sidebar.tsx, ClauseTreeItem.tsx, SectionHeader.tsx, ModeCard.tsx
StatusPill.tsx, MethodPill.tsx, AdvisoryDrawer.tsx, advisories.ts
NavPathColumnPicker.tsx — N:1 drill-down column picker for filter / apply
KvGrid.tsx, Sortable.tsx, SegmentedToggle.tsx, …
shell/ — frame / header / URL bar / tabs / drawers
Frame.tsx, FrameHeader.tsx, ModeShell.tsx
UrlBar.tsx, MainTabs.tsx, SettingsDrawer.tsx
state/ — per-mode state shapes + persistence
readState.ts, writeState.ts, relateState.ts, executeState.ts, binaryState.ts
savedRequests.ts — localStorage persistence (per-org scope)
SaveContext.tsx — cross-mode save/load context
displaySettings.ts — display preferences
registry/ — request-type catalogue
requestTypes.ts — 17 request types grouped into 5 groups
views/ — output panes
CodeView.tsx — Code tab: xrm/xrm-batch/fetch/curl/powershell
ResultsView.tsx — tabs (Detail/Grid/JSON/Headers) + error state
results/ — grid + cell renderers + flattening
detail/ — record detail card
theme/ — Fluent theme + studio-owned brand vars📋 Web API coverage
| Operation | Status |
|---|---|
| Retrieve Multiple (full OData) | ✅ |
| Retrieve Single (by GUID / alt key) | ✅ |
| @odata.nextLink paging | ✅ |
| Predefined / user queries | ✅ |
| $apply (groupby / aggregate) | ✅ |
| Lambda filter (any / all) | ✅ |
| $expand (single + collection, nested) | ✅ |
| Polymorphic lookups (Customer, Owner, regardingobjectid) | ✅ |
| Create / Update / Upsert / Delete | ✅ |
| Prefer: return=representation | ✅ |
| Optimistic concurrency (If-Match ETag) | ✅ |
| MSCRM.* bypass family (custom plug-ins, flows, async) | ✅ |
| Single-property PUT / DELETE | ✅ |
| Alternate-key upsert | ✅ |
| Associate / Disassociate (collection + single-valued) | ✅ |
| Merge (account / contact / incident) | ✅ |
| Execute Action (OOB / Custom API / Custom Action) | ✅ |
| Execute Function | ✅ |
| Execute Workflow | ✅ |
| File column (upload / download / delete) | ✅ |
| Image column (thumbnail / full-size) | ✅ |
| Attachment / Note (inline + message-based) | ✅ |
| $batch envelope composer | ⏳ (see roadmap) |
| CreateMultiple / UpdateMultiple / DeleteMultiple | ⏳ |
| BulkDelete async job | ⏳ |
| ExecuteMultiple envelope | ⏳ |
🤝 Contributing
See docs/CONTRIBUTING.md for branch conventions, commit style, code style, repo layout, and PR guidance.
📋 Roadmap
$batchenvelope composer mode- Bulk-write modes (
CreateMultiple,UpdateMultiple,DeleteMultiple) BulkDeleteasync-job composerExecuteMultiplerequest envelope- Recent-runs click-to-reload
- WriteResultCard "Retrieve this record" cross-mode jump
- EntitySpecific param — reference vs new-instance toggle
- Workflow input parameters (parse XAML
InArgument) - Predefined Query — apply to related collection
- CSV / JSON export from results grid
- Column resize with persisted widths
- Smoke-test suite covering URL builders + code generators
📄 License
Licensed under the GNU Affero General Public License v3.0 (AGPL-3.0-only). See LICENSE for details.
🙏 Acknowledgments
- Dataverse REST Builder - Original inspiration.
- Power Platform ToolBox — host platform for the desktop / web app where DRS runs.
- FetchXML Studio — sibling tool that pioneered many of the patterns reused here (metadata cache, scope settings, results grid).
- Fluent UI — UI component library.
- Monaco Editor — VS Code's editor core.
Built with ❤️ for the Power Platform community.
