@mohsinonxrm/pptb-fetchxml-studio 1.3.1 β†’ 1.4.1

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 CHANGED
@@ -2,34 +2,67 @@
2
2
 
3
3
  A powerful, modern FetchXML query builder and data explorer for [Power Platform ToolBox](https://github.com/PowerPlatformToolBox/desktop-app). Inspired by the XrmToolBox FetchXML Builder, reimagined with React 18, Fluent UI v9, and seamless Dataverse integration.
4
4
 
5
- ![Version](https://img.shields.io/badge/version-1.3.0-blue)
5
+ ![Version](https://img.shields.io/badge/version-1.4.1-blue)
6
+ [![npm](https://img.shields.io/npm/v/@mohsinonxrm/pptb-fetchxml-studio?label=npm)](https://www.npmjs.com/package/@mohsinonxrm/pptb-fetchxml-studio)
6
7
  ![React](https://img.shields.io/badge/React-18-61DAFB?logo=react)
7
8
  ![TypeScript](https://img.shields.io/badge/TypeScript-5.9-3178C6?logo=typescript)
8
9
  ![Fluent UI](https://img.shields.io/badge/Fluent%20UI-v9-0078D4?logo=microsoft)
9
- ![PPTB Types](https://img.shields.io/badge/%40pptb%2Ftypes-1.2.0-orange)
10
+ ![PPTB Types](https://img.shields.io/badge/%40pptb%2Ftypes-1.2.3-blue)
10
11
 
11
12
  ## ✨ Features
12
13
 
13
14
  ### πŸ”§ Visual Query Builder
14
15
  - **Tree-based hierarchy** β€” Build FetchXML queries visually with entities, attributes, filters, orders, and link-entities
15
- - **Smart property editors** β€” Context-aware panels for each node type (fetch options, conditions, relationships)
16
+ - **Property panel for every node** β€” Fetch options, entity, attributes, all-attributes, sorts, filters, conditions and link-entities each have an editor covering the full FetchXML reference. Every field explains itself in an info popover with a link to Microsoft Learn.
17
+ - **Metadata-driven** β€” Attribute pickers, operators, value pickers, aggregate functions, date grouping, sortable attributes and compare-to-attribute choices follow each attribute's metadata, so the panel only offers what Dataverse accepts.
18
+ - **Shows problems where they are** β€” Rule errors appear on the field they're about and as an **Error** badge in the tree; settings Dataverse ignores are listed with a **Remove** button.
19
+ - **Tree badges** β€” Aggregate, Distinct, Retained and Hints on the query; Group by, date grouping and aggregate functions on attributes; Union and Quick find on filters.
20
+ - **Safe mode changes** β€” Turning off aggregate, or switching a link-entity to a type that returns no attributes, lists what has to change and asks first.
16
21
  - **Full FetchXML support** β€” Aggregate queries, grouping, distinct, top/count, paging, and advanced hints
17
22
  - **Nested filters** β€” Create complex AND/OR filter groups with unlimited nesting
18
- - **Link-entity relationships** β€” Browse and add 1:N, N:1, and N:N relationships with inner/outer joins
23
+ - **Link-entity relationships** β€” Browse and add 1:N, N:1, and N:N relationships with inner/outer joins; N:N relationships join through their intersect entity, as documented; intersect entities (such as `systemuserroles` or `teammembership`) can be joined to each side of their N:N relationship
19
24
 
20
25
  ### πŸ“Š Results Grid
21
26
  - **Virtualized DataGrid** β€” Handle large result sets with smooth scrolling (powered by react-window)
27
+ - **Paging and Retrieve All** β€” Scrolling to the end loads the next page; **Retrieve All** loads every page, with **Stop** to keep what's loaded so far. Pages use the documented paging cookie and the query's own `count` and starting `page`. Queries Dataverse returns no cookie for (sorted by a link-entity attribute, or distinct) page by page number, with a note. Views follow `@odata.nextLink`. A failed page shows how many rows loaded, with **Retry**
22
28
  - **Rich cell rendering** β€” Power Apps-style display for lookups, option sets, dates, currency, and more
23
- - **Multi-column sorting** β€” Click headers to sort; Shift+click for multi-column sort
24
- - **Row selection** β€” Select single or multiple records for bulk operations
29
+ - **Multi-column sorting** β€” Click headers to sort; Shift+click for multi-column sort. Each click writes a valid `<order>`: by alias in aggregate queries, with `entityname` for link-entity columns, and not at all for attribute types Dataverse can't sort (multi-select option sets)
30
+ - **Edit / Add columns** β€” Remove, reorder and add columns from the root entity or related entities (N:1 and 1:N); the tree and FetchXML change to match. Removing a column removes its attribute (aliased or not, root or link-entity), any sort on its alias, and an outer link-entity left empty. At least one column must remain (a query without attributes returns every attribute). Add columns isn't available in aggregate queries
31
+ - **Link-entities without an alias** β€” Their columns show in the grid: Dataverse names them `{name}{N}.attribute`, and the grid uses the same names
32
+ - **Row selection** β€” Select single or multiple records for bulk operations. Record actions use the table's primary key, which Dataverse returns automatically for regular queries; for aggregate and distinct queries (which return no record IDs) record actions are disabled with an explanation
25
33
  - **Resizable & reorderable columns** β€” Customize your view with drag-and-drop columns
26
34
  - **Formatted values** β€” Display OData formatted values or raw values (configurable)
35
+ - **Works with every entity** β€” Queries run against the entity set name from Dataverse metadata, so entities with irregular names (for example `feedback`, `msdyn_conversationdata`) run correctly. The Power Platform ToolBox host's own name guess is wrong for about 4% of entities.
27
36
 
28
37
  ### πŸ“ FetchXML Editor
29
38
  - **Monaco editor** β€” Full-featured XML editor with XML syntax highlighting and line numbers
30
39
  - **Bi-directional editing** β€” Edit XML directly and parse back to the visual builder
40
+ - **Intellisense from the reference** β€” Element, attribute and value suggestions follow the FetchXML reference (every documented attribute, including `aggregatelimit`, `datasource`, `options`, `rowaggregate`, order `entityname` and the quick find overrides). Undocumented attributes are suggested only where Dataverse was verified to accept them, and say so. Value suggestions work for hyphenated attributes such as `link-type`
41
+ - **Power Automate–safe values** β€” Apostrophes stay literal in the generated FetchXML (attribute values use double quotes), so expressions such as `@{body('x')}` paste into Power Automate unchanged; `"`, `&`, `<` and `>` are escaped everywhere, including names, aliases and `from` / `to`
42
+ - **Editor mode actions run what you see** β€” In editor mode, **Execute**, **Save** and **Send to Tool** first load your FetchXML into the tree, so results, saved views and sent queries always match the editor. Execute is available as soon as the editor has content, even before an entity is chosen. If the tree would change anything, a confirmation lists the changes first, and the editor then shows the cleaned FetchXML that ran. Invalid FetchXML is reported and nothing runs.
43
+ - **What you build is what runs** β€” The generated FetchXML contains exactly what is in the tree; nothing is injected, so what you copy, save as a view, or send to another tool matches what executes
31
44
  - **Copy to clipboard** β€” One-click copy of generated FetchXML, available in both read-only and editor modes
32
- - **Alias validation** β€” Invalid alias characters are sanitized in real time in the property editors; the parser auto-corrects aliases on Parse to Tree with a clear description of each correction
45
+ - **Alias validation** β€” Invalid alias characters are sanitized in real time in the property editors; Parse to Tree corrects invalid aliases, lists each correction, and updates every reference to a corrected alias (condition and order `entityname`, `valueof`, aggregate order aliases) so the query keeps working
46
+ - **Accurate validation** β€” Everything in the FetchXML reference is supported, plus undocumented behaviour verified to work (e.g. `link-type="natural"`). Every rule is checked against a live Dataverse environment:
47
+ - **errors** block execution β€” only for FetchXML Dataverse is known to reject;
48
+ - **warnings** offer **Execute anyway** β€” only where it can't be determined in the client (e.g. a link-entity without `from`/`to`, which is valid only for system many-to-many relationships);
49
+ - **notes** never block (e.g. `xmlns:*` from other tools, which Dataverse ignores).
50
+ - **Query-level checks** β€” Validate and Execute catch the mistakes Dataverse rejects, each verified on a live environment:
51
+ - aggregate queries where an attribute has no Group by / aggregate or no alias;
52
+ - an aggregate order using an alias from another entity;
53
+ - `aggregatelimit` combined with an order;
54
+ - duplicate aliases;
55
+ - an invalid union hint;
56
+ - misuse of `rowaggregate`;
57
+ - `top` together with `count`, `page` or `returntotalrecordcount`, and `page` below 1;
58
+ - comparing to another attribute (`valueof`) with an operator other than eq, ne, gt, ge, lt or le, or through a link-entity without an alias;
59
+ - a link-entity inside a filter whose type isn't any / not any / all / not all;
60
+ - attributes, all-attributes or orders in an exists, in, any, not any, all or not all link-entity.
61
+
62
+ Settings Dataverse accepts but ignores (such as `aggregatelimit` on a non-aggregate query, `aggregate` on a condition, which FetchXML can't use to filter on aggregate values, or `visible` on a link-entity) are shown as notes only.
63
+ - **Boolean spellings** β€” `"1"`/`"0"` and other spellings Dataverse accepts are read correctly (e.g. `usertimezone="1"`, `descending="1"`, `enableprefiltering="1"`), so Parse to Tree never flips or drops them.
64
+ - **Lossless Parse to Tree** β€” Every attribute in the FetchXML reference is kept, including `datasource`, `aggregatelimit`, `useraworderby`, query hints (`options`), `latematerialize`, `<order alias>`, `distinct` counts, `rowaggregate` and quick find flags. So are unknown, namespaced and legacy attributes (e.g. `xmlns:generator` from SQL 4 CDS, `version`/`mapping` from Advanced Find, `uiname`/`uitype` from saved views). Every FetchXML example in the Microsoft Learn FetchXML articles round-trips without loss.
65
+ - **No silent changes** β€” If Parse to Tree must correct something Dataverse would reject (for example an invalid alias), a confirmation dialog lists every change first
33
66
  - **LayoutXML preview** β€” See the column layout configuration
34
67
 
35
68
  ### πŸ“₯ Load & Save Views
@@ -49,7 +82,7 @@ A powerful, modern FetchXML query builder and data explorer for [Power Platform
49
82
  - **Run workflows** β€” Execute on-demand workflows on selected records with batch execution and progress tracking
50
83
 
51
84
  ### πŸ“‹ Bulk Attribute Selection
52
- - **Select Attributes dialog** β€” Multi-select attributes from a searchable DataGrid; accessible from entity and link-entity context menus
85
+ - **Select Attributes dialog** β€” Multi-select attributes from a sortable, resizable grid (display name, logical name, type). Long names end with an ellipsis and show in full on hover. Search by display or logical name, filter by type, show selected only, and see how many are selected. Opens from the entity and link-entity Add menus
53
86
  - **Smart updates** β€” Adds new selections and removes deselected attributes while preserving existing order and properties
54
87
  - **Search & filter** β€” Real-time filtering across logical name, display name, and data type columns
55
88
 
@@ -59,22 +92,27 @@ A powerful, modern FetchXML query builder and data explorer for [Power Platform
59
92
  - **C# FetchExpression** β€” Simple `new FetchExpression(@"...")` wrapper using a verbatim string literal
60
93
  - **JavaScript** β€” `Xrm.WebApi.retrieveMultipleRecords` call for model-driven apps and PCF components
61
94
  - **pac CLI** β€” `pac org fetch --xml "..."` command for the Power Platform CLI
62
- - **Power Automate** β€” Field-by-field form mirroring the Dataverse "List rows" connector UI; table name resolved from `EntityDefinitions` metadata (not a heuristic), each field individually copyable
63
- - **Web API** β€” Full OData URL with request headers, cURL snippet, and PowerShell `Invoke-RestMethod` example; entity set name resolved from metadata
95
+ - **Power Automate** β€” Field-by-field form mirroring the Dataverse "List rows" connector UI; table name resolved from `EntityDefinitions` metadata (not a heuristic), each field individually copyable; Top Count is taken only from `top` (`count` is a page size)
96
+ - **Web API** β€” Full OData URL with request headers (including `Prefer: odata.include-annotations="*"` for formatted values and paging cookies), the documented paging-cookie steps, a cURL snippet and a PowerShell `Invoke-RestMethod` example; entity set name resolved from metadata
64
97
  - **SQL** β€” Dataverse-generated T-SQL via `FetchXMLToSQL` (undocumented API, labelled Preview)
65
98
 
66
99
  ### 🎨 User Experience
67
- - **Dark/Light themes** β€” Follows Power Platform ToolBox theme with Fluent UI tokens
100
+ - **Dark/Light themes** β€” Every surface (tree, properties, editors, results grid, panels and dialogs) uses the Fluent UI v9 light or dark theme and follows the Power Platform ToolBox theme live: changing it in ToolBox settings, including **System**, and changing the OS theme while ToolBox is on System, update the tool without a reload
68
101
  - **Lazy metadata loading** β€” Loads only what's needed, when it's needed
69
102
  - **Intelligent caching** β€” In-memory cache prevents duplicate API calls per session
70
103
  - **Resizable panes** β€” Adjust split-pane layout to your preference
71
104
  - **Keyboard shortcuts** β€” `Ctrl+Enter` to execute query, copy XML to clipboard
72
105
  - **Display settings** β€” Toggle logical names vs display names in column headers; choose formatted, raw, or both value modes in the grid and exports
73
106
  - **Query scope settings** β€” Control which entities appear in the entity picker: *Publisher + Solution* (full filter), *Solution Only*, or *All Entities* (no filter). Persisted across sessions.
74
- - **Advanced Find Only toggle** β€” When on (default), limits entities and attributes to those marked `IsValidForAdvancedFind = true`. Disable to access all entities including system and developer tables. Filter is applied locally β€” toggling is instant with no additional API call.
107
+ - **Advanced Find Only toggle** β€” When on (default), entities, attributes and relationships are limited to the ones Advanced Find shows (`IsValidForAdvancedFind`), in every entity scope. Turn it off to build queries Advanced Find can't: system and customization entities (saved views, forms, plug-in steps, solution components), intersect entities (team membership, user roles), and hidden attributes and relationships. Attributes that can't be read (`IsValidForRead = false`) are never listed. Hidden label attributes (such as `statuscodename`) can be filtered and sorted. Toggling is instant: metadata is fetched once and filtered locally. Attributes already in a query keep their operators and value inputs either way.
108
+
109
+ ### πŸ”— Tool-to-Tool (T2T) Integration
110
+ - **Send to Tool button** β€” Launch another PPTB tool directly from FetchXML Studio, pre-loading it with the current FetchXML query and active Dataverse connection (one-way handoff).
111
+ - **Automatic tool discovery** β€” Targets are discovered via the PPTB capability registry β€” installed tools that declare the `fetchxml` capability. The Send to Tool control always renders as a dropdown listing the discovered tools (even a single match). No configuration needed.
112
+ - **Active connection forwarding** β€” The active Dataverse connection is forwarded automatically so the target tool opens against the same environment.
113
+ - **Inbound prefill** β€” FetchXML Studio also accepts incoming T2T invocations from other tools (see [Callee Contract](#-callee-contract-pptbconfigjson) below).
75
114
 
76
115
  ### πŸ”’ Privilege-Aware
77
- - **Security checks** β€” Validates user privileges before every destructive or restricted operation
78
116
  - **Export privilege check** β€” Only shows Dataverse export option if user has access
79
117
  - **Delete privilege check** β€” Validates entity-specific delete permissions before enabling delete actions
80
118
  - **Bulk delete privilege check** β€” Validates `prvBulkDelete` before surfacing bulk delete
@@ -85,7 +123,7 @@ A powerful, modern FetchXML query builder and data explorer for [Power Platform
85
123
 
86
124
  ```
87
125
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
88
- β”‚ [Entity Selector β–Ό] [Load View β–Ό] [Save View] [βš™ Settings]β”‚
126
+ β”‚ [Entity Selector β–Ό] [Load View β–Ό] [Save View] [Send to Tool β–Ό] [βš™] β”‚
89
127
  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
90
128
  β”‚ β”‚ [FetchXML] [LayoutXML] [Results] [Code] [β–Ά Execute]β”‚
91
129
  β”‚ Query Tree β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
@@ -114,7 +152,7 @@ A powerful, modern FetchXML query builder and data explorer for [Power Platform
114
152
  - Connection to a Dataverse environment
115
153
 
116
154
  ### Installation
117
- FetchXML Studio is available as a tool in Power Platform ToolBox. Install it from the tool gallery or load as a custom tool.
155
+ FetchXML Studio is available as a tool in Power Platform ToolBox. Install it from the tool gallery or load as a custom tool. The package is published on npm as [`@mohsinonxrm/pptb-fetchxml-studio`](https://www.npmjs.com/package/@mohsinonxrm/pptb-fetchxml-studio).
118
156
 
119
157
  ## πŸ› οΈ Development
120
158
 
@@ -122,11 +160,11 @@ FetchXML Studio is available as a tool in Power Platform ToolBox. Install it fro
122
160
 
123
161
  ```bash
124
162
  # Clone the repository
125
- git clone https://github.com/mohsinonxrm/pptb-fetchxml-studio.git
126
- cd pptb-fetchxml-studio
163
+ git clone https://github.com/mohsinonxrm/pptb-mxrm-fetchxml-studio.git
164
+ cd pptb-mxrm-fetchxml-studio
127
165
 
128
- # Install dependencies
129
- npm install
166
+ # Install the exact dependency versions from package-lock.json
167
+ npm ci
130
168
 
131
169
  # Start development server
132
170
  npm run dev
@@ -144,10 +182,28 @@ npm run preview
144
182
  # Validate package manifest against PPTB registry rules
145
183
  npm run validate
146
184
 
147
- # Finalize for publishing (build + shrinkwrap)
185
+ # Finalize for publishing (validate + build)
148
186
  npm run finalize-package
149
187
  ```
150
188
 
189
+ **Tool icon.** `package.json` points `icon` at `icons/icon.svg` under `dist/`, where Power Platform ToolBox looks for it. The build bundles `icons/icon-insider.svg` (gold stamp) for prerelease versions such as `1.4.1-beta.8`, and `icons/icon.svg` for releases.
190
+
191
+ **Dependencies.** `package-lock.json` is committed and pins every dependency with an integrity hash. Use `npm ci` for a clean install. After changing dependencies, run `npm install` and commit the updated lockfile.
192
+
193
+ ### Testing
194
+
195
+ ```bash
196
+ # Unit tests (Vitest + jsdom): parser, serializer, rules, round-trip over the Microsoft Learn examples
197
+ npm test
198
+
199
+ # Watch mode / coverage
200
+ npm run test:watch
201
+ npm run coverage
202
+
203
+ # Lint
204
+ npm run lint
205
+ ```
206
+
151
207
  ### Debug Logging
152
208
 
153
209
  Enable debug logging in the browser console:
@@ -175,11 +231,12 @@ disableAllDebug();
175
231
  | **React** | 18.3 | UI framework |
176
232
  | **TypeScript** | 5.9 | Type-safe development |
177
233
  | **Vite** | 7 | Build tooling and HMR |
178
- | **Fluent UI v9** | 9.72 | Microsoft design system (Tree, DataGrid, Tabs, Drawer, etc.) |
234
+ | **Fluent UI v9** | 9.74 | Microsoft design system (Tree, DataGrid, Tabs, Drawer, etc.) |
179
235
  | **Monaco Editor** | 0.54 | VS Code XML editor for FetchXML authoring |
180
236
  | **react-window** | 2 | Virtualized list rendering for large datasets |
181
237
  | **ExcelJS** | 4.4 | Native `.xlsx` generation with typed cells |
182
- | **@pptb/types** | 1.2.0 | Power Platform ToolBox host API types (`window.dataverseAPI`, `window.toolboxAPI`) |
238
+ | **@pptb/types** | 1.2.3 | Power Platform ToolBox host API types (`window.dataverseAPI`, `window.toolboxAPI`) |
239
+ | **Vitest** | 5 | Unit tests (jsdom) |
183
240
 
184
241
  ## πŸ“ Project Structure
185
242
 
@@ -192,16 +249,21 @@ src/
192
249
  β”‚ β”‚ β”œβ”€β”€ pptbClient.ts # window.dataverseAPI wrapper (all Dataverse ops)
193
250
  β”‚ β”‚ β”œβ”€β”€ dataverseMetadata.ts # Lazy metadata loading with cache + dedup
194
251
  β”‚ β”‚ β”œβ”€β”€ excelExport.ts # Local ExcelJS export with native types
195
- β”‚ β”‚ └── formattedValues.ts # OData @FormattedValue annotation helpers
252
+ β”‚ β”‚ β”œβ”€β”€ formattedValues.ts # OData @FormattedValue annotation helpers
253
+ β”‚ β”‚ └── invocation.ts # T2T caller API (isT2TSupported, sendFetchXmlToTool)
196
254
  β”‚ β”œβ”€β”€ model/
197
255
  β”‚ β”‚ β”œβ”€β”€ nodes.ts # FetchXML node TypeScript definitions
198
256
  β”‚ β”‚ β”œβ”€β”€ fetchxml.ts # FetchXML XML generation
199
- β”‚ β”‚ β”œβ”€β”€ fetchxmlParser.ts # FetchXML XML β†’ node tree parser
257
+ β”‚ β”‚ β”œβ”€β”€ fetchxmlParser.ts # FetchXML XML β†’ node tree parser (severity-classified findings)
258
+ β”‚ β”‚ β”œβ”€β”€ fetchxmlRules.ts # Query-level rules shared by the parser and editors
259
+ β”‚ β”‚ β”œβ”€β”€ attributeCapabilities.ts # What each attribute type supports (sort, group by, aggregates, valueof)
260
+ β”‚ β”‚ β”œβ”€β”€ aggregateToggle.ts # What turning off an aggregate query changes
200
261
  β”‚ β”‚ β”œβ”€β”€ fetchxmlIntellisense.ts # Intellisense / autocomplete helpers
201
262
  β”‚ β”‚ β”œβ”€β”€ layoutxml.ts # LayoutXML generation
202
263
  β”‚ β”‚ β”œβ”€β”€ operators.ts # Operator definitions by attribute type
203
264
  β”‚ β”‚ β”œβ”€β”€ displaySettings.ts # Display settings types and defaults
204
- β”‚ β”‚ └── treeUtils.ts # Tree traversal utilities
265
+ β”‚ β”‚ β”œβ”€β”€ treeUtils.ts # Tree traversal utilities
266
+ β”‚ β”‚ └── __fixtures__/ # FetchXML examples from Microsoft Learn (MIT) for round-trip tests
205
267
  β”‚ β”œβ”€β”€ engine/
206
268
  β”‚ β”‚ β”œβ”€β”€ queryExpressionTypes.ts # QE JSON schema interfaces (number | string enums)
207
269
  β”‚ β”‚ β”œβ”€β”€ queryExpressionCodegen.ts # C# SDK code emitter (resolveEnum, unwrapODataValue)
@@ -212,10 +274,16 @@ src/
212
274
  β”‚ └── ui/
213
275
  β”‚ β”œβ”€β”€ LeftPane/ # Tree view + context-aware properties panel
214
276
  β”‚ β”‚ └── PropertiesPanel/
215
- β”‚ β”‚ └── editors/ # Node-specific property editors
277
+ β”‚ β”‚ β”œβ”€β”€ editors/ # One editor per node type
278
+ β”‚ β”‚ β”œβ”€β”€ fields.tsx # InfoField, InfoCheckbox, NumberField, LeftoverSetting
279
+ β”‚ β”‚ β”œβ”€β”€ docLinks.ts # Microsoft Learn links used by the info popovers
280
+ β”‚ β”‚ β”œβ”€β”€ ruleFindings.ts # Rule findings β†’ field validation
281
+ β”‚ β”‚ └── useEditorMetadata.ts # Attribute / entity metadata hooks
216
282
  β”‚ β”œβ”€β”€ RightPane/ # Monaco editor, LayoutXML viewer, results grid,
217
283
  β”‚ β”‚ # code generation panel, Power Automate pane
218
- β”‚ β”œβ”€β”€ Toolbar/ # Entity selector, load view picker, save button
284
+ β”‚ β”œβ”€β”€ Toolbar/ # Entity selector, load view picker, save button,
285
+ β”‚ β”‚ # send-to-tool button
286
+
219
287
  β”‚ β”œβ”€β”€ Dialogs/ # Save view, select attributes, delete, bulk
220
288
  β”‚ β”‚ # delete, workflow picker, solution picker
221
289
  β”‚ └── Settings/ # Settings drawer (display preferences)
@@ -237,21 +305,84 @@ src/
237
305
  | All-attributes | βœ… |
238
306
  | Filters (and/or) | βœ… |
239
307
  | Nested filters | βœ… |
240
- | All condition operators | βœ… |
308
+ | All condition operators (offered per attribute from the documented data types and a live operator Γ— attribute-type matrix; value inputs match each operator: whole numbers for "x" operators, period + year for fiscal period operators) | βœ… |
241
309
  | Link-entity (joins) | βœ… |
242
310
  | Inner/outer joins | βœ… |
243
- | N:N relationships | βœ… |
311
+ | N:N relationships (through the intersect entity) | βœ… |
244
312
  | Orders (sorting) | βœ… |
245
313
  | Multi-column sort | βœ… |
246
314
  | Aggregate queries | βœ… |
247
315
  | Groupby | βœ… |
248
316
  | Distinct | βœ… |
249
317
  | Top/Count | βœ… |
250
- | Paging with cookies | βœ… |
318
+ | Paging with cookies (`fetchxmlpagingcookie`), page-number paging when Dataverse returns no cookie, views via `@odata.nextLink` | βœ… |
251
319
  | Value-of conditions | βœ… |
252
320
  | Entity name on conditions | βœ… |
253
321
  | Filter link-entity (any/all) | βœ… |
254
322
  | Query hints | βœ… |
323
+ | Order by alias (aggregate) | βœ… |
324
+ | Quick find, union hint | βœ… |
325
+ | Row aggregate (hierarchy child count) | βœ… |
326
+ | Long-term retention (`datasource`) | βœ… |
327
+ | Validation against Dataverse rules | βœ… |
328
+
329
+ ## πŸ”— Callee Contract (`pptb.config.json`)
330
+
331
+ FetchXML Studio declares a PPTB [Inter-Tool Invocation](https://github.com/PowerPlatformToolBox/desktop-app) callee contract in `pptb.config.json` at the repository root, and declares the `fetchxml` capability so callers can discover it. Any other PPTB tool can launch FetchXML Studio and pre-populate it by passing a prefill payload that matches the following schema:
332
+
333
+ ```json
334
+ {
335
+ "fetchXml": "<fetch><entity name=\"account\">...</entity></fetch>"
336
+ }
337
+ ```
338
+
339
+ | Field | Type | Required | Description |
340
+ |---|---|---|---|
341
+ | `fetchXml` | `string` | One of these three | Full serialized FetchXML query string. The root entity is parsed from this. |
342
+ | `viewRef` | `object` | One of these three | EntityReference to an existing view. The FetchXML is retrieved from the view record. |
343
+ | `entityLogicalName` | `string` | One of these three | Root entity logical name (e.g. `"account"`). Starts a fresh query on that entity. **Ignored when `fetchXml` is present** β€” the root entity is then derived from the FetchXML, since a mismatched value would break this metadata-driven tool's lookups. |
344
+ | `viewRef.id` | `string` (uuid) | Required if viewRef | GUID of the view record |
345
+ | `viewRef.entityLogicalName` | `"savedquery"` \| `"userquery"` | Required if viewRef | Dataverse entity logical name β€” `savedquery` = system/public view, `userquery` = personal view |
346
+
347
+ Resolution priority at runtime: **`fetchXml` β†’ `viewRef` β†’ `entityLogicalName`**. With no prefill (a standalone launch) the tool opens empty.
348
+
349
+ ### Returning a query to the caller
350
+
351
+ When FetchXML Studio is launched by another tool, a **Return FetchXML** button appears in the FetchXML toolbar. Clicking it returns the current query to the caller:
352
+
353
+ ```json
354
+ { "fetchXml": "<fetch>...</fetch>" }
355
+ ```
356
+
357
+ The caller receives this as the resolved value of `launchTool(...)`. This is distinct from the host-injected "Return to [Caller]" banner, which simply navigates back and resolves the caller's promise with `null` (no data).
358
+
359
+ ### Sending a query from another PPTB tool
360
+
361
+ To launch FetchXML Studio from your own PPTB tool with a pre-loaded query:
362
+
363
+ ```typescript
364
+ const result = await window.toolboxAPI.invocation.launchTool(
365
+ "@mohsinonxrm/pptb-fetchxml-studio",
366
+ { fetchXml: "<fetch><entity name=\"account\"><attribute name=\"name\"/></entity></fetch>" },
367
+ { primaryConnectionId: connection?.id ?? null },
368
+ );
369
+
370
+ // If the user clicks "Return FetchXML", result is { fetchXml }.
371
+ // If they close the window or click the host's back banner, result is null.
372
+ const editedFetchXml = (result as { fetchXml?: string } | null)?.fetchXml;
373
+ ```
374
+
375
+ ### Sending a query to another tool (Send to Tool)
376
+
377
+ FetchXML Studio discovers send targets automatically β€” no configuration needed. It calls the host capability registry (`toolboxAPI.invocation.findToolsByCapability("fetchxml")`) to find installed tools that declare the `fetchxml` capability in their own `pptb.config.json`, and lists them in a **Send to Tool** button (a single button for one match, a dropdown picker for several). The button is hidden when no other `fetchxml`-capable tools are installed, or on hosts that don't support capability discovery.
378
+
379
+ The launch is one-way (`noReturn: true`) β€” FetchXML Studio hands the current query off and does not wait for data back. Any tool that wants to appear here only needs to declare:
380
+
381
+ ```json
382
+ { "invocation": { "version": "1.0.0", "capabilities": ["fetchxml"], "prefill": { "properties": { "fetchXml": { "type": "string" } } } } }
383
+ ```
384
+
385
+ ---
255
386
 
256
387
  ## 🀝 Contributing
257
388
 
@@ -276,7 +407,6 @@ This project follows [Conventional Commits](https://www.conventionalcommits.org/
276
407
  - [ ] Import/export query definitions (JSON)
277
408
  - [ ] Quick query templates
278
409
  - [ ] Query history
279
- - [ ] Web API code generation
280
410
  - [ ] Query performance insights
281
411
  - [ ] Syntax validation in Monaco
282
412
 
package/SECURITY.md ADDED
@@ -0,0 +1,13 @@
1
+ # Security Policy
2
+
3
+ ## Reporting a Vulnerability
4
+
5
+ Please do **not** open a public issue.
6
+ Use GitHub Security Advisories: <https://github.com/mohsinonxrm/pptb-mxrm-fetchxml-studio/security/advisories/new>
7
+
8
+ ## Scope
9
+
10
+ - Authentication flows
11
+ - Token handling
12
+ - Dataverse request logging
13
+ - Dependency vulnerabilities