@sabrish/power-platform-solution-blueprint 0.5.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/CHANGELOG.md +166 -0
- package/LICENSE +21 -0
- package/README.md +331 -0
- package/dist/assets/BusinessProcessFlowDiscovery-BSDgBoIA.js +2 -0
- package/dist/assets/BusinessProcessFlowDiscovery-BSDgBoIA.js.map +1 -0
- package/dist/assets/ColumnSecurityDiscovery-B3yVn0LZ.js +2 -0
- package/dist/assets/ColumnSecurityDiscovery-B3yVn0LZ.js.map +1 -0
- package/dist/assets/ConnectionReferenceDiscovery-DFTQuo-S.js +2 -0
- package/dist/assets/ConnectionReferenceDiscovery-DFTQuo-S.js.map +1 -0
- package/dist/assets/CustomAPIDiscovery-BiQS_ENt.js +2 -0
- package/dist/assets/CustomAPIDiscovery-BiQS_ENt.js.map +1 -0
- package/dist/assets/CustomConnectorDiscovery-TvNjElP3.js +2 -0
- package/dist/assets/CustomConnectorDiscovery-TvNjElP3.js.map +1 -0
- package/dist/assets/EnvironmentVariableDiscovery-DG_tXCcQ.js +2 -0
- package/dist/assets/EnvironmentVariableDiscovery-DG_tXCcQ.js.map +1 -0
- package/dist/assets/GlobalChoiceDiscovery-nivz9yGs.js +2 -0
- package/dist/assets/GlobalChoiceDiscovery-nivz9yGs.js.map +1 -0
- package/dist/assets/HtmlReporter-Cu9NwA6b.js +1894 -0
- package/dist/assets/HtmlReporter-Cu9NwA6b.js.map +1 -0
- package/dist/assets/JsonReporter-BqDJorX-.js +2 -0
- package/dist/assets/JsonReporter-BqDJorX-.js.map +1 -0
- package/dist/assets/MarkdownReporter-B1FEpc6-.js +53 -0
- package/dist/assets/MarkdownReporter-B1FEpc6-.js.map +1 -0
- package/dist/assets/ZipPackager-B4xx8LbF.js +3 -0
- package/dist/assets/ZipPackager-B4xx8LbF.js.map +1 -0
- package/dist/assets/_baseUniq-Nm59UrQz.js +2 -0
- package/dist/assets/_baseUniq-Nm59UrQz.js.map +1 -0
- package/dist/assets/arc-BVr5R-xB.js +2 -0
- package/dist/assets/arc-BVr5R-xB.js.map +1 -0
- package/dist/assets/architectureDiagram-VXUJARFQ-C-olLUHb.js +37 -0
- package/dist/assets/architectureDiagram-VXUJARFQ-C-olLUHb.js.map +1 -0
- package/dist/assets/blockDiagram-VD42YOAC-B4incSX8.js +123 -0
- package/dist/assets/blockDiagram-VD42YOAC-B4incSX8.js.map +1 -0
- package/dist/assets/c4Diagram-YG6GDRKO-BJxhcb17.js +11 -0
- package/dist/assets/c4Diagram-YG6GDRKO-BJxhcb17.js.map +1 -0
- package/dist/assets/channel-CjhOgOS9.js +2 -0
- package/dist/assets/channel-CjhOgOS9.js.map +1 -0
- package/dist/assets/chunk-4BX2VUAB-BIe6iK-b.js +2 -0
- package/dist/assets/chunk-4BX2VUAB-BIe6iK-b.js.map +1 -0
- package/dist/assets/chunk-55IACEB6-CA6P5Ey6.js +2 -0
- package/dist/assets/chunk-55IACEB6-CA6P5Ey6.js.map +1 -0
- package/dist/assets/chunk-B4BG7PRW-licIQLj-.js +166 -0
- package/dist/assets/chunk-B4BG7PRW-licIQLj-.js.map +1 -0
- package/dist/assets/chunk-DI55MBZ5-CxFpzZDS.js +221 -0
- package/dist/assets/chunk-DI55MBZ5-CxFpzZDS.js.map +1 -0
- package/dist/assets/chunk-FMBD7UC4-VjnBPAq9.js +16 -0
- package/dist/assets/chunk-FMBD7UC4-VjnBPAq9.js.map +1 -0
- package/dist/assets/chunk-QN33PNHL-BcMwEixv.js +2 -0
- package/dist/assets/chunk-QN33PNHL-BcMwEixv.js.map +1 -0
- package/dist/assets/chunk-QZHKN3VN-6eOG0UZ6.js +2 -0
- package/dist/assets/chunk-QZHKN3VN-6eOG0UZ6.js.map +1 -0
- package/dist/assets/chunk-TZMSLE5B-B38vDpWG.js +2 -0
- package/dist/assets/chunk-TZMSLE5B-B38vDpWG.js.map +1 -0
- package/dist/assets/classDiagram-2ON5EDUG-CEjpoRuI.js +2 -0
- package/dist/assets/classDiagram-2ON5EDUG-CEjpoRuI.js.map +1 -0
- package/dist/assets/classDiagram-v2-WZHVMYZB-CEjpoRuI.js +2 -0
- package/dist/assets/classDiagram-v2-WZHVMYZB-CEjpoRuI.js.map +1 -0
- package/dist/assets/clone-FgHYDKfb.js +2 -0
- package/dist/assets/clone-FgHYDKfb.js.map +1 -0
- package/dist/assets/cose-bilkent-S5V4N54A-BFz6nI_Q.js +2 -0
- package/dist/assets/cose-bilkent-S5V4N54A-BFz6nI_Q.js.map +1 -0
- package/dist/assets/cytoscape.esm-CyJtwmzi.js +332 -0
- package/dist/assets/cytoscape.esm-CyJtwmzi.js.map +1 -0
- package/dist/assets/dagre-6UL2VRFP-B9xCTGce.js +5 -0
- package/dist/assets/dagre-6UL2VRFP-B9xCTGce.js.map +1 -0
- package/dist/assets/defaultLocale-DX6XiGOO.js +2 -0
- package/dist/assets/defaultLocale-DX6XiGOO.js.map +1 -0
- package/dist/assets/diagram-PSM6KHXK-BG-mPBdn.js +25 -0
- package/dist/assets/diagram-PSM6KHXK-BG-mPBdn.js.map +1 -0
- package/dist/assets/diagram-QEK2KX5R-BAd41szY.js +44 -0
- package/dist/assets/diagram-QEK2KX5R-BAd41szY.js.map +1 -0
- package/dist/assets/diagram-S2PKOQOG-Qhy7nrMX.js +25 -0
- package/dist/assets/diagram-S2PKOQOG-Qhy7nrMX.js.map +1 -0
- package/dist/assets/erDiagram-Q2GNP2WA-HJD-sygr.js +61 -0
- package/dist/assets/erDiagram-Q2GNP2WA-HJD-sygr.js.map +1 -0
- package/dist/assets/flowDiagram-NV44I4VS-DcFzPV5l.js +163 -0
- package/dist/assets/flowDiagram-NV44I4VS-DcFzPV5l.js.map +1 -0
- package/dist/assets/ganttDiagram-JELNMOA3-5hzmn_YM.js +268 -0
- package/dist/assets/ganttDiagram-JELNMOA3-5hzmn_YM.js.map +1 -0
- package/dist/assets/gitGraphDiagram-NY62KEGX-BJoaRoQP.js +66 -0
- package/dist/assets/gitGraphDiagram-NY62KEGX-BJoaRoQP.js.map +1 -0
- package/dist/assets/graph-iTkoc_Qp.js +2 -0
- package/dist/assets/graph-iTkoc_Qp.js.map +1 -0
- package/dist/assets/index-C0N3Q-wf.js +404 -0
- package/dist/assets/index-C0N3Q-wf.js.map +1 -0
- package/dist/assets/index-Dau71uup.css +1 -0
- package/dist/assets/infoDiagram-WHAUD3N6-Dm-9Locf.js +3 -0
- package/dist/assets/infoDiagram-WHAUD3N6-Dm-9Locf.js.map +1 -0
- package/dist/assets/init-Gi6I4Gst.js +2 -0
- package/dist/assets/init-Gi6I4Gst.js.map +1 -0
- package/dist/assets/journeyDiagram-XKPGCS4Q-Cn4q49x5.js +140 -0
- package/dist/assets/journeyDiagram-XKPGCS4Q-Cn4q49x5.js.map +1 -0
- package/dist/assets/kanban-definition-3W4ZIXB7-lUx8-faE.js +90 -0
- package/dist/assets/kanban-definition-3W4ZIXB7-lUx8-faE.js.map +1 -0
- package/dist/assets/katex-CBSAILhF.js +262 -0
- package/dist/assets/katex-CBSAILhF.js.map +1 -0
- package/dist/assets/layout-BNMvCxLN.js +2 -0
- package/dist/assets/layout-BNMvCxLN.js.map +1 -0
- package/dist/assets/linear-OqJg3PPm.js +2 -0
- package/dist/assets/linear-OqJg3PPm.js.map +1 -0
- package/dist/assets/min-N_DZlU1c.js +2 -0
- package/dist/assets/min-N_DZlU1c.js.map +1 -0
- package/dist/assets/mindmap-definition-VGOIOE7T-C43AGCOz.js +69 -0
- package/dist/assets/mindmap-definition-VGOIOE7T-C43AGCOz.js.map +1 -0
- package/dist/assets/ordinal-Cboi1Yqb.js +2 -0
- package/dist/assets/ordinal-Cboi1Yqb.js.map +1 -0
- package/dist/assets/pieDiagram-ADFJNKIX-COl1BPcc.js +31 -0
- package/dist/assets/pieDiagram-ADFJNKIX-COl1BPcc.js.map +1 -0
- package/dist/assets/quadrantDiagram-AYHSOK5B-C-fSINkS.js +8 -0
- package/dist/assets/quadrantDiagram-AYHSOK5B-C-fSINkS.js.map +1 -0
- package/dist/assets/requirementDiagram-UZGBJVZJ-DSag9Z4m.js +65 -0
- package/dist/assets/requirementDiagram-UZGBJVZJ-DSag9Z4m.js.map +1 -0
- package/dist/assets/sankeyDiagram-TZEHDZUN-JyvYJ7BV.js +11 -0
- package/dist/assets/sankeyDiagram-TZEHDZUN-JyvYJ7BV.js.map +1 -0
- package/dist/assets/sequenceDiagram-WL72ISMW-GkUPZQOc.js +146 -0
- package/dist/assets/sequenceDiagram-WL72ISMW-GkUPZQOc.js.map +1 -0
- package/dist/assets/stateDiagram-FKZM4ZOC-DiJHFrza.js +2 -0
- package/dist/assets/stateDiagram-FKZM4ZOC-DiJHFrza.js.map +1 -0
- package/dist/assets/stateDiagram-v2-4FDKWEC3-BgdSYitm.js +2 -0
- package/dist/assets/stateDiagram-v2-4FDKWEC3-BgdSYitm.js.map +1 -0
- package/dist/assets/timeline-definition-IT6M3QCI-BIeoFfU5.js +62 -0
- package/dist/assets/timeline-definition-IT6M3QCI-BIeoFfU5.js.map +1 -0
- package/dist/assets/treemap-KMMF4GRG-BDDqLxLB.js +129 -0
- package/dist/assets/treemap-KMMF4GRG-BDDqLxLB.js.map +1 -0
- package/dist/assets/xychartDiagram-PRI3JC2R-CUzWjNyv.js +8 -0
- package/dist/assets/xychartDiagram-PRI3JC2R-CUzWjNyv.js.map +1 -0
- package/dist/index.html +13 -0
- package/index.html +12 -0
- package/package.json +74 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Power Platform Solution Blueprint will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.5.1] - 2026-02-10
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
**Repository Structure**
|
|
13
|
+
- Restructured repository from monorepo to flat structure to comply with PPTB tool development standards
|
|
14
|
+
- Moved `packages/pptb-tool/*` to root directory for proper PPTB Desktop integration
|
|
15
|
+
- Moved `packages/core/src/*` to `src/core/` maintaining logical separation
|
|
16
|
+
- Updated all 47 source files with corrected import paths (replaced workspace references with relative paths)
|
|
17
|
+
- Consolidated package.json files (removed workspace configuration)
|
|
18
|
+
- Preserved documentation and screenshots during restructure
|
|
19
|
+
|
|
20
|
+
**API Integration**
|
|
21
|
+
- Integrated official `@pptb/types` package (v1.0.19) from npm registry
|
|
22
|
+
- Updated all code to use `window.dataverseAPI` global (official PPTB Desktop API structure)
|
|
23
|
+
- Removed custom type definitions in favor of official types
|
|
24
|
+
- Updated `PptbDataverseClient` to accept `DataverseAPI.API` directly instead of nested structure
|
|
25
|
+
- Updated all components to use `await window.toolboxAPI.getToolContext()` (async method)
|
|
26
|
+
|
|
27
|
+
**Documentation**
|
|
28
|
+
- Updated `docs/API_SECURITY.md` to reflect official @pptb/types integration
|
|
29
|
+
- Clarified API connection mechanism and authentication flow
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
**Export Consistency**
|
|
34
|
+
- Fixed ERD exports to generate single comprehensive diagram instead of 3 diagrams
|
|
35
|
+
- HTML exports now show 1 ERD matching the in-tool UI behavior
|
|
36
|
+
- Markdown exports now use only the first (comprehensive) ERD diagram
|
|
37
|
+
- Eliminated redundant publisher-specific diagrams in exported documentation
|
|
38
|
+
|
|
39
|
+
**PPTB Desktop Compatibility**
|
|
40
|
+
- Added `base: './'` to `vite.config.ts` to fix dynamic import path resolution
|
|
41
|
+
- Resolved "Failed to fetch dynamically imported module" error with custom `pptb-webview://` protocol
|
|
42
|
+
- All asset paths now use relative paths compatible with PPTB Desktop's webview environment
|
|
43
|
+
|
|
44
|
+
### Added
|
|
45
|
+
|
|
46
|
+
**User Interface**
|
|
47
|
+
- Added author attribution to footer ("by SAB" with link to GitHub profile)
|
|
48
|
+
- Enhanced footer with GitHub repository link and Open icon
|
|
49
|
+
|
|
50
|
+
### Technical Notes
|
|
51
|
+
|
|
52
|
+
**Breaking Changes**
|
|
53
|
+
- Repository structure changed from monorepo to flat layout
|
|
54
|
+
- Import paths changed from workspace references (`@ppsb/core`) to relative paths (`./core` or `../core`)
|
|
55
|
+
- API initialization requires awaiting `getToolContext()` instead of synchronous access
|
|
56
|
+
- `PptbDataverseClient` constructor signature changed to accept `DataverseAPI.API`
|
|
57
|
+
|
|
58
|
+
**Migration Guide**
|
|
59
|
+
- If extending this codebase, use relative imports instead of workspace package references
|
|
60
|
+
- Use `window.dataverseAPI` for Dataverse operations instead of `window.toolboxAPI.dataverse`
|
|
61
|
+
- Always await `window.toolboxAPI.getToolContext()` for tool context access
|
|
62
|
+
|
|
63
|
+
**Dependencies**
|
|
64
|
+
- Added `@pptb/types@^1.0.19` as devDependency (official PPTB Desktop type definitions)
|
|
65
|
+
- Maintained all existing runtime dependencies (React, Fluent UI, Mermaid, JSZip)
|
|
66
|
+
|
|
67
|
+
[0.5.1]: https://github.com/sabrish/power-platform-solution-blueprint/releases/tag/v0.5.1
|
|
68
|
+
## [0.5.0] - 2026-02-08
|
|
69
|
+
|
|
70
|
+
### Added
|
|
71
|
+
|
|
72
|
+
**Discovery & Documentation**
|
|
73
|
+
- Complete entity schema discovery (fields, relationships, keys, alternate keys)
|
|
74
|
+
- Plugin discovery with full registration details and execution order analysis
|
|
75
|
+
- Power Automate flow discovery with trigger analysis and external call detection
|
|
76
|
+
- Business rule discovery and parsing (client-side and server-side)
|
|
77
|
+
- Classic workflow discovery with migration recommendations and risk assessment
|
|
78
|
+
- Business Process Flow documentation with stage and step details
|
|
79
|
+
- JavaScript web resource analysis with external call detection and deprecated API warnings (Xrm.Page)
|
|
80
|
+
- Custom API documentation with complete parameter specifications
|
|
81
|
+
- Environment variable discovery and value tracking (critical for ALM)
|
|
82
|
+
- Connection reference discovery with premium connector detection
|
|
83
|
+
- Global choice (shared option set) documentation
|
|
84
|
+
- Security role discovery with entity permissions and special permissions matrices
|
|
85
|
+
- Field security profile discovery
|
|
86
|
+
- Attribute masking rule discovery
|
|
87
|
+
- Column security profile discovery
|
|
88
|
+
|
|
89
|
+
**Analysis & Visualization**
|
|
90
|
+
- Entity Relationship Diagram (ERD) generation using Mermaid with publisher color-coding
|
|
91
|
+
- Execution pipeline visualization showing client → sync → async automation flow per entity
|
|
92
|
+
- Cross-entity automation mapping to identify automation that affects multiple entities
|
|
93
|
+
- External dependency detection and risk analysis (categorized as Trusted/Known/Unknown)
|
|
94
|
+
- Solution distribution analysis showing component breakdown across solutions
|
|
95
|
+
- Entity complexity scoring based on field count, automation density, and relationships
|
|
96
|
+
- Performance risk identification (synchronous plugins with external calls, execution chains)
|
|
97
|
+
- Deprecated API detection (classic workflows, Xrm.Page usage in JavaScript)
|
|
98
|
+
|
|
99
|
+
**Export Formats**
|
|
100
|
+
- Markdown export with complete file structure ready for Azure DevOps Wiki
|
|
101
|
+
- JSON export for programmatic use, baseline storage, and CI/CD integration
|
|
102
|
+
- HTML single-page export with interactive navigation, search, and accordion sections
|
|
103
|
+
- ZIP packaging bundling all formats with organized directory structure
|
|
104
|
+
|
|
105
|
+
**User Interface**
|
|
106
|
+
- Smart scope selection (by publisher, solution, or all entities)
|
|
107
|
+
- Multi-select dropdowns with live filtering and selected item management
|
|
108
|
+
- Real-time progress tracking during generation with phase indicators
|
|
109
|
+
- Interactive results dashboard with tabs and expandable sections
|
|
110
|
+
- ERD view with zoom/pan and entity quick links
|
|
111
|
+
- Entity detail view with complete schema, automation, and execution pipeline
|
|
112
|
+
- Security roles view with interactive accordion and permission matrices
|
|
113
|
+
- Export dialog with format selection and download management
|
|
114
|
+
- Comprehensive error handling with user-friendly messages and retry options
|
|
115
|
+
- Accessibility compliance (WCAG AA standards)
|
|
116
|
+
- Loading states with spinners and progress indicators
|
|
117
|
+
- Responsive design (desktop optimized, mobile functional)
|
|
118
|
+
|
|
119
|
+
**Technical Infrastructure**
|
|
120
|
+
- Monorepo structure with `@ppsb/core` (pure TypeScript) and `@ppsb/pptb-tool` (React UI)
|
|
121
|
+
- PPTB Desktop integration via `window.toolboxAPI`
|
|
122
|
+
- TypeScript 5.x with strict mode and comprehensive type definitions
|
|
123
|
+
- React 18 with Vite 5 for fast development and optimized builds
|
|
124
|
+
- Fluent UI v9 component library (Microsoft Design System)
|
|
125
|
+
- Mermaid diagram rendering for ERD and execution pipelines
|
|
126
|
+
- JSZip for multi-file export packages
|
|
127
|
+
- Performance optimizations (batched queries, in-memory filtering, lazy rendering)
|
|
128
|
+
- Error handling and retry logic for Dataverse API calls
|
|
129
|
+
|
|
130
|
+
### Development Notes
|
|
131
|
+
- Developed with significant AI collaboration:
|
|
132
|
+
- **ChatGPT (OpenAI)**: Initial brainstorming, concept validation, feature ideation
|
|
133
|
+
- **Claude (Anthropic)**: Detailed architecture design, comprehensive implementation via Claude Code
|
|
134
|
+
- Built on modern web technologies: TypeScript 5.x, React 18, Fluent UI v9, Vite 5
|
|
135
|
+
- Clean architecture with core business logic separated from UI concerns
|
|
136
|
+
- Extensible design for future enhancements and custom analyzers
|
|
137
|
+
|
|
138
|
+
### Known Limitations
|
|
139
|
+
- **Canvas Apps**: Metadata only (description, connection references) due to API limitations
|
|
140
|
+
- **Custom Pages**: Metadata only (no component-level analysis available)
|
|
141
|
+
- **Power Pages**: Only included if deployed to Dataverse (site settings, web files)
|
|
142
|
+
- **Customer Insights - Journeys**: Not included in v0.5 (planned for future version)
|
|
143
|
+
- **Baseline Comparison**: Planned for v0.6 (compare current state against saved JSON baseline)
|
|
144
|
+
- **CLI Tool**: Planned for v0.6 (command-line interface for automation)
|
|
145
|
+
- **CI/CD Integration**: Planned for v0.6 (GitHub Actions, Azure DevOps tasks)
|
|
146
|
+
- **Plugin Code Analysis**: Not available (requires source code access, most plugins are compiled DLLs)
|
|
147
|
+
- **Custom Connector Details**: Metadata only (OpenAPI spec not accessible via Dataverse API)
|
|
148
|
+
- **Large Environments**: May take 10-20 minutes for environments with 500+ entities and complex automation
|
|
149
|
+
|
|
150
|
+
### Performance Considerations
|
|
151
|
+
- **Query Optimization**: Batched queries reduce API calls from thousands to hundreds
|
|
152
|
+
- **GUID Normalization**: Proper GUID handling prevents silent failures in OData filters
|
|
153
|
+
- **Selective $select**: Only requests needed fields to minimize payload size
|
|
154
|
+
- **Strategic $expand**: Fetches related data when always needed to reduce round-trips
|
|
155
|
+
- **In-Memory Filtering**: Client-side filtering when server-side options are limited
|
|
156
|
+
- **Lazy Rendering**: Large tables and diagrams render only when viewed
|
|
157
|
+
- **Progress Tracking**: Real-time feedback during long-running operations
|
|
158
|
+
|
|
159
|
+
### Security & Privacy
|
|
160
|
+
- **No Data Storage**: PPSB never stores your Dataverse data on external servers
|
|
161
|
+
- **Local Processing**: All analysis happens client-side in your browser
|
|
162
|
+
- **Export Control**: You control where exported files are saved
|
|
163
|
+
- **Connection Security**: Uses PPTB Desktop's existing authenticated connection
|
|
164
|
+
- **No Telemetry**: No usage data collected or transmitted
|
|
165
|
+
|
|
166
|
+
[0.5.0]: https://github.com/sabrish/power-platform-solution-blueprint/releases/tag/v0.5.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SAB
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
# Power Platform Solution Blueprint (PPSB)
|
|
2
|
+
|
|
3
|
+
> Complete architectural blueprints for your Power Platform solutions.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+

|
|
7
|
+
|
|
8
|
+
Generate comprehensive technical documentation for Dataverse and Power Platform solutions with automated discovery, analysis, and export capabilities.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 🎯 What is PPSB?
|
|
13
|
+
|
|
14
|
+
**PPSB** is an automated documentation tool that generates complete architectural blueprints for Power Platform solutions. It discovers, analyzes, and documents your entire Dataverse environment—from entity schemas to automation pipelines—saving architects and developers hundreds of hours of manual documentation work.
|
|
15
|
+
|
|
16
|
+
The tool generates **comprehensive blueprints** including Entity Relationship Diagrams (ERD), complete entity documentation with automation execution pipelines, cross-entity automation mapping, external dependency analysis, and solution distribution breakdowns. All exports are production-ready: Markdown files ready for Azure DevOps Wiki, JSON for programmatic analysis, and self-contained HTML for stakeholder sharing.
|
|
17
|
+
|
|
18
|
+
PPSB benefits **solution architects** (system documentation), **developers** (onboarding and reference), **governance teams** (compliance and dependency tracking), and **consultants** (assessment and migration planning). Primary use cases include technical documentation generation, governance and compliance auditing, performance analysis, and migration planning.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## ✨ Key Features
|
|
23
|
+
|
|
24
|
+
<details>
|
|
25
|
+
<summary><b>📋 Discovery & Documentation</b> (click to expand)</summary>
|
|
26
|
+
|
|
27
|
+
- **Entities**: Complete schema (fields, relationships, keys, alternate keys)
|
|
28
|
+
- **Plugins**: Full registration details with execution order analysis
|
|
29
|
+
- **Power Automate Flows**: Trigger analysis, connection references, external calls
|
|
30
|
+
- **Business Rules**: Client-side and server-side with condition parsing
|
|
31
|
+
- **Classic Workflows**: Documentation with migration recommendations and risk assessment
|
|
32
|
+
- **Business Process Flows**: Stage and step documentation
|
|
33
|
+
- **JavaScript Web Resources**: Code analysis with external call detection and deprecated API warnings
|
|
34
|
+
- **Custom APIs**: Complete parameter documentation and privilege requirements
|
|
35
|
+
- **Environment Variables**: Value tracking critical for ALM
|
|
36
|
+
- **Connection References**: Premium connector detection for licensing
|
|
37
|
+
- **Global Choices**: Shared option sets with usage tracking
|
|
38
|
+
- **Security**: Roles, field security profiles, attribute masking, column security
|
|
39
|
+
|
|
40
|
+
</details>
|
|
41
|
+
|
|
42
|
+
<details>
|
|
43
|
+
<summary><b>📊 Analysis & Visualization</b> (click to expand)</summary>
|
|
44
|
+
|
|
45
|
+
- **Entity Relationship Diagram (ERD)**: Mermaid diagram with publisher color-coding and relationship types
|
|
46
|
+
- **Execution Pipeline**: Visual representation of client → sync → async automation flow per entity
|
|
47
|
+
- **Cross-Entity Automation**: Map automation that creates/updates records across entity boundaries
|
|
48
|
+
- **External Dependencies**: Detect and risk-assess all external API calls (flows, plugins, web resources)
|
|
49
|
+
- **Solution Distribution**: Component breakdown across solutions with dependency mapping
|
|
50
|
+
- **Entity Complexity**: Scoring based on field count, automation density, and relationship complexity
|
|
51
|
+
- **Performance Risks**: Identify synchronous plugins with external calls, long execution chains
|
|
52
|
+
|
|
53
|
+
</details>
|
|
54
|
+
|
|
55
|
+
<details>
|
|
56
|
+
<summary><b>💾 Export Formats</b> (click to expand)</summary>
|
|
57
|
+
|
|
58
|
+
- **Markdown**: Complete file structure ready for Azure DevOps Wiki upload
|
|
59
|
+
- **JSON**: Full data export for programmatic use, baselines, and CI/CD pipelines
|
|
60
|
+
- **HTML**: Single-page interactive document with navigation, search, and accordion sections
|
|
61
|
+
- **ZIP**: All formats bundled together with organized directory structure
|
|
62
|
+
|
|
63
|
+
</details>
|
|
64
|
+
|
|
65
|
+
<details>
|
|
66
|
+
<summary><b>🔍 Smart Scope Selection</b> (click to expand)</summary>
|
|
67
|
+
|
|
68
|
+
- **By Publisher**: Select one or more publishers to document all their solutions
|
|
69
|
+
- **By Solution**: Choose specific solutions regardless of publisher
|
|
70
|
+
- **By Entity**: Document all entities (with optional system entity inclusion)
|
|
71
|
+
- **Filtering**: Multi-select with live filtering and selected item management
|
|
72
|
+
|
|
73
|
+
</details>
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 🚀 Quick Start
|
|
78
|
+
|
|
79
|
+
1. **Install**: Add PPSB to your PPTB Desktop tools directory
|
|
80
|
+
2. **Launch**: Open PPSB from PPTB Desktop with an active Dataverse connection
|
|
81
|
+
3. **Select Scope**: Choose publisher(s), solution(s), or all entities
|
|
82
|
+
4. **Generate**: Click "Generate Blueprint" and wait for discovery and analysis
|
|
83
|
+
5. **Review**: Explore the interactive results with ERD, entity details, and automation analysis
|
|
84
|
+
6. **Export**: Download as Markdown, JSON, HTML, or ZIP bundle
|
|
85
|
+
7. **Use**: Upload Markdown to Azure DevOps Wiki or share HTML with stakeholders
|
|
86
|
+
|
|
87
|
+
📖 **[Detailed User Guide](docs/user-guide.md)** |
|
|
88
|
+
📊 **[Example Output Structure](docs/examples/example-output-structure.md)** |
|
|
89
|
+
🏗️ **[Architecture Documentation](docs/architecture.md)**
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 📸 Screenshots
|
|
94
|
+
|
|
95
|
+
### Scope Selection
|
|
96
|
+
Choose what to document - by publisher or solution with multi-select filtering.
|
|
97
|
+
|
|
98
|
+

|
|
99
|
+
|
|
100
|
+
### Confirmation Screen
|
|
101
|
+
Review your selection before generating the blueprint.
|
|
102
|
+
|
|
103
|
+

|
|
104
|
+
|
|
105
|
+
### Results Dashboard
|
|
106
|
+
Interactive dashboard with summary metrics, ERD visualization, and component browser.
|
|
107
|
+
|
|
108
|
+

|
|
109
|
+
|
|
110
|
+
### Component Details
|
|
111
|
+
Browse and explore all discovered components with expandable details and inline documentation.
|
|
112
|
+
|
|
113
|
+

|
|
114
|
+
|
|
115
|
+
### Solution Distribution Analysis
|
|
116
|
+
Analyze component distribution across solutions with dependency mapping and shared component detection.
|
|
117
|
+
|
|
118
|
+

|
|
119
|
+
|
|
120
|
+
### External Dependencies Analysis
|
|
121
|
+
Identify and assess all external API calls across flows, plugins, and web resources with risk scoring.
|
|
122
|
+
|
|
123
|
+

|
|
124
|
+
|
|
125
|
+
### Export Options
|
|
126
|
+
Choose your export format - Markdown, JSON, HTML, or ZIP bundle.
|
|
127
|
+
|
|
128
|
+

|
|
129
|
+
|
|
130
|
+
### HTML Export
|
|
131
|
+
Self-contained, interactive HTML documentation ready to share with stakeholders.
|
|
132
|
+
|
|
133
|
+

|
|
134
|
+
|
|
135
|
+

|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 💡 Use Cases
|
|
140
|
+
|
|
141
|
+
### Architecture Documentation
|
|
142
|
+
- **Onboarding**: Generate complete system docs for new team members joining projects
|
|
143
|
+
- **Proposals**: Create technical specifications and architecture diagrams for client proposals
|
|
144
|
+
- **Handover**: Provide comprehensive documentation during project transitions
|
|
145
|
+
|
|
146
|
+
### Governance & Compliance
|
|
147
|
+
- **External Dependencies**: Identify all external API calls for security review and approval
|
|
148
|
+
- **Premium Connectors**: Track premium connector usage for accurate licensing compliance
|
|
149
|
+
- **Environment Variables**: Monitor configuration management and ALM readiness
|
|
150
|
+
- **Security Audit**: Document all security roles, field security, and data masking rules
|
|
151
|
+
|
|
152
|
+
### Performance Analysis
|
|
153
|
+
- **Bottleneck Detection**: Find synchronous plugins with external calls blocking transactions
|
|
154
|
+
- **Execution Chains**: Identify complex automation sequences that may cause performance issues
|
|
155
|
+
- **Optimization Planning**: Prioritize refactoring based on complexity scores and risk levels
|
|
156
|
+
|
|
157
|
+
### Migration Planning
|
|
158
|
+
- **Classic Workflow Assessment**: Identify all workflows requiring migration to cloud flows
|
|
159
|
+
- **Complexity Analysis**: Get detailed migration recommendations with step-by-step guidance
|
|
160
|
+
- **Dependency Mapping**: Understand cross-entity automation before migration
|
|
161
|
+
|
|
162
|
+
📖 **[More Use Cases in User Guide](docs/user-guide.md#common-use-cases)**
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## 🔧 Technical Stack
|
|
167
|
+
|
|
168
|
+
- **Language**: TypeScript 5.x (strict mode)
|
|
169
|
+
- **UI Framework**: React 18 + Vite 5
|
|
170
|
+
- **Component Library**: Fluent UI v9 (Microsoft Design System)
|
|
171
|
+
- **Diagrams**: Mermaid (ERD and execution pipelines)
|
|
172
|
+
- **Export**: JSZip (multi-file packages)
|
|
173
|
+
- **Architecture**: Flat structure with separated core logic (`src/core/`) and React UI (`src/components/`)
|
|
174
|
+
|
|
175
|
+
🏗️ **[Architecture Details](docs/architecture.md)** |
|
|
176
|
+
🛣️ **[Roadmap](docs/roadmap.md)**
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## 🚦 Installation
|
|
181
|
+
|
|
182
|
+
### For PPTB Desktop Users
|
|
183
|
+
|
|
184
|
+
1. Available in the PPTB Marketplace
|
|
185
|
+
|
|
186
|
+
### For Development
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
# Clone repository
|
|
190
|
+
git clone https://github.com/sabrish/power-platform-solution-blueprint.git
|
|
191
|
+
cd power-platform-solution-blueprint
|
|
192
|
+
|
|
193
|
+
# Install dependencies
|
|
194
|
+
pnpm install
|
|
195
|
+
|
|
196
|
+
# Run development server
|
|
197
|
+
pnpm dev
|
|
198
|
+
|
|
199
|
+
# Build for production
|
|
200
|
+
pnpm build
|
|
201
|
+
|
|
202
|
+
# Type check
|
|
203
|
+
pnpm typecheck
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**Development Requirements:**
|
|
207
|
+
- Node.js 18+ and pnpm 8+
|
|
208
|
+
- PPTB Desktop for testing (connects to Dataverse)
|
|
209
|
+
- Windows, macOS, or Linux
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## 📚 Documentation
|
|
214
|
+
|
|
215
|
+
Comprehensive documentation for all aspects of PPSB:
|
|
216
|
+
|
|
217
|
+
- **[User Guide](docs/user-guide.md)** - Complete usage instructions from scope selection to export
|
|
218
|
+
- **[Architecture](docs/architecture.md)** - Technical design, component structure, and extension points
|
|
219
|
+
- **[API Security](docs/API_SECURITY.md)** - Complete API reference, security considerations, and data access documentation
|
|
220
|
+
- **[Roadmap](docs/roadmap.md)** - Future versions (baseline comparison, CLI, CI/CD integration)
|
|
221
|
+
- **[Examples](docs/examples/)** - Sample outputs and documentation structure
|
|
222
|
+
- [Output Structure](docs/examples/example-output-structure.md)
|
|
223
|
+
- [Sample ERD](docs/examples/sample-erd.md)
|
|
224
|
+
- [Sample Entity Documentation](docs/examples/sample-entity-doc.md)
|
|
225
|
+
- **[Changelog](CHANGELOG.md)** - Version history and release notes
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## 🙏 Credits & Acknowledgments
|
|
230
|
+
|
|
231
|
+
### Development Team
|
|
232
|
+
- **Primary Developer**: [SAB](https://github.com/sabrish)
|
|
233
|
+
|
|
234
|
+
### AI Collaboration
|
|
235
|
+
|
|
236
|
+
This project was developed with significant AI assistance:
|
|
237
|
+
|
|
238
|
+
- **ChatGPT (OpenAI)**: Initial brainstorming, concept validation, feature ideation, and requirements exploration. Helped refine the vision for what PPSB should be and validated technical approaches.
|
|
239
|
+
|
|
240
|
+
- **Claude (Anthropic)**: Detailed architecture design, comprehensive implementation planning, and hands-on code development via **Claude Code**. Provided deep technical expertise for TypeScript/React implementation, Dataverse API integration, performance optimization strategies, and accessibility compliance.
|
|
241
|
+
|
|
242
|
+
The combination of AI tools accelerated development while maintaining high code quality and architectural consistency. ChatGPT helped explore possibilities and refine the product vision, while Claude provided meticulous technical implementation with thousands of lines of production-ready code.
|
|
243
|
+
|
|
244
|
+
**AI Development Approach:**
|
|
245
|
+
- Architecture designed through iterative collaboration with Claude
|
|
246
|
+
- Implementation executed via Claude Code (AI-assisted coding)
|
|
247
|
+
- Code reviews and optimization suggestions from both AI assistants
|
|
248
|
+
- Documentation structure and content refined with AI input
|
|
249
|
+
|
|
250
|
+
### Technology
|
|
251
|
+
|
|
252
|
+
- **PPTB Desktop** - [Power Platform Toolbox](https://www.powerplatformtoolbox.com/) (platform for PPSB deployment)
|
|
253
|
+
- **Microsoft Dataverse** - Data platform and APIs
|
|
254
|
+
- **Fluent UI** - Microsoft's design system and React components
|
|
255
|
+
- **Mermaid** - Diagram rendering engine for ERD and pipeline visualizations
|
|
256
|
+
- **TypeScript** - Language and type system
|
|
257
|
+
- **React** - UI framework
|
|
258
|
+
- **Vite** - Build tool and development server
|
|
259
|
+
|
|
260
|
+
### Open Source Libraries
|
|
261
|
+
|
|
262
|
+
- JSZip (ZIP file generation)
|
|
263
|
+
- React Router (navigation)
|
|
264
|
+
- Various TypeScript utilities and type definitions
|
|
265
|
+
|
|
266
|
+
### Community
|
|
267
|
+
|
|
268
|
+
Thanks to the Power Platform community for inspiration, feedback, and the collective knowledge that shaped PPSB's feature set and use cases.
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## 🛣️ Roadmap Highlights
|
|
273
|
+
|
|
274
|
+
PPSB is under active development with exciting features planned:
|
|
275
|
+
|
|
276
|
+
**Baseline Comparison & Automation**
|
|
277
|
+
- Baseline comparison (load previous JSON and detect changes)
|
|
278
|
+
- Change detection with risk assessment
|
|
279
|
+
- Command-line interface (CLI) for automation
|
|
280
|
+
- CI/CD integration (GitHub Actions, Azure DevOps)
|
|
281
|
+
|
|
282
|
+
**Enhanced Analysis**
|
|
283
|
+
- Impact analysis ("What breaks if I delete this?")
|
|
284
|
+
- Unused component detection
|
|
285
|
+
- Business process mining
|
|
286
|
+
- Custom analysis rules
|
|
287
|
+
|
|
288
|
+
**Extended Platform Support**
|
|
289
|
+
- Canvas Apps (enhanced with screen analysis)
|
|
290
|
+
- Power Pages (forms, lists, Liquid templates)
|
|
291
|
+
- Customer Insights & Marketing (journeys, segments, email templates)
|
|
292
|
+
- Additional component types (30+ new types)
|
|
293
|
+
|
|
294
|
+
**Enterprise Features**
|
|
295
|
+
- Performance benchmarking
|
|
296
|
+
- Multi-environment comparison
|
|
297
|
+
- Multi-tenant support
|
|
298
|
+
- Advanced visualizations and AI-powered insights
|
|
299
|
+
|
|
300
|
+
📖 **[Full Roadmap](docs/roadmap.md)**
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## 📄 License
|
|
305
|
+
|
|
306
|
+
[MIT License](LICENSE)
|
|
307
|
+
|
|
308
|
+
Copyright © 2026 SAB. All rights reserved.
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## 💬 Support
|
|
313
|
+
|
|
314
|
+
- **Issues**: [GitHub Issues](https://github.com/sabrish/power-platform-solution-blueprint/issues) - Report bugs or request features
|
|
315
|
+
- **Discussions**: [GitHub Discussions](https://github.com/sabrish/power-platform-solution-blueprint/discussions) - Ask questions and share ideas
|
|
316
|
+
- **Documentation**: [docs/](docs/) - Comprehensive guides and examples
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## 🌟 Quick Links
|
|
321
|
+
|
|
322
|
+
- [Installation](#installation)
|
|
323
|
+
- [User Guide](docs/user-guide.md)
|
|
324
|
+
- [Architecture](docs/architecture.md)
|
|
325
|
+
- [Roadmap](docs/roadmap.md)
|
|
326
|
+
- [Changelog](CHANGELOG.md)
|
|
327
|
+
- [Examples](docs/examples/)
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
**Made with ❤️ for the Power Platform community**
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var g=Object.defineProperty;var p=(a,e,t)=>e in a?g(a,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):a[e]=t;var m=(a,e,t)=>p(a,typeof e!="symbol"?e+"":e,t);class f{constructor(e,t){m(this,"client");m(this,"onProgress");this.client=e,this.onProgress=t}async getBusinessProcessFlowsByIds(e){if(e.length===0)return[];try{const n=[];for(let s=0;s<e.length;s+=20){const c=`(${e.slice(s,s+20).map(l=>`workflowid eq ${l.replace(/[{}]/g,"")}`).join(" or ")}) and category eq 4`,i=await this.client.query("workflows",{select:["workflowid","name","description","category","uniquename","primaryentity","statecode","xaml","ismanaged","createdon","modifiedon","_ownerid_value"],filter:c,orderBy:["primaryentity asc","name asc"]});n.push(...i.value),this.onProgress&&this.onProgress(n.length,e.length)}return n.map(s=>this.mapToBusinessProcessFlow(s))}catch(t){throw new Error(`Failed to retrieve Business Process Flows: ${t instanceof Error?t.message:"Unknown error"}`)}}mapToBusinessProcessFlow(e){const t=this.parseXaml(e.xaml);return{id:e.workflowid,name:e.name,description:e.description,primaryEntity:e.primaryentity,primaryEntityDisplayName:e["_primaryentity_value@OData.Community.Display.V1.FormattedValue"]||null,state:this.getStateName(e.statecode),stateCode:e.statecode,isManaged:e.ismanaged,uniqueName:e.uniquename,xaml:e.xaml,definition:t,owner:e["_ownerid_value@OData.Community.Display.V1.FormattedValue"]||"Unknown",ownerId:e._ownerid_value||"",modifiedBy:e["_modifiedby_value@OData.Community.Display.V1.FormattedValue"]||"Unknown",modifiedOn:e.modifiedon,createdOn:e.createdon}}parseXaml(e){const t=[];try{const n=/<mxswa:Stage[^>]*EntityName="([^"]+)"[^>]*StageName="([^"]+)"[^>]*StageId="([^"]+)"[^>]*>/gi;let s,u=0;for(;(s=n.exec(e))!==null;){const o=s[1],d=s[2],y=s[3];t.push({id:y,name:d,entity:o,order:u++,steps:[]})}const r=[...new Set(t.map(o=>o.entity))],c=/<mxswa:Step[^>]*>/gi,i=e.match(c),l=i?i.length:0;return{stages:t,entities:r,totalSteps:l,crossEntityFlow:r.length>1}}catch(n){return{stages:[],entities:[],totalSteps:0,crossEntityFlow:!1,parseError:n instanceof Error?n.message:"Failed to parse XAML"}}}getStateName(e){switch(e){case 0:return"Draft";case 1:return"Active";default:return"Draft"}}}export{f as BusinessProcessFlowDiscovery};
|
|
2
|
+
//# sourceMappingURL=BusinessProcessFlowDiscovery-BSDgBoIA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BusinessProcessFlowDiscovery-BSDgBoIA.js","sources":["../../src/core/discovery/BusinessProcessFlowDiscovery.ts"],"sourcesContent":["import type { IDataverseClient } from '../dataverse/IDataverseClient.js';\nimport type { BusinessProcessFlow } from '../types/businessProcessFlow.js';\n\n/**\n * Raw BPF data from Dataverse\n */\ninterface RawBusinessProcessFlow {\n workflowid: string;\n name: string;\n description: string | null;\n category: number; // Must be 4 for BPFs\n uniquename: string;\n primaryentity: string;\n statecode: number;\n xaml: string;\n ismanaged: boolean;\n createdon: string;\n modifiedon: string;\n _ownerid_value?: string;\n '_ownerid_value@OData.Community.Display.V1.FormattedValue'?: string;\n '_modifiedby_value@OData.Community.Display.V1.FormattedValue'?: string;\n '_primaryentity_value@OData.Community.Display.V1.FormattedValue'?: string;\n}\n\n/**\n * Discovers Business Process Flows (category=4)\n */\nexport class BusinessProcessFlowDiscovery {\n private readonly client: IDataverseClient;\n private onProgress?: (current: number, total: number) => void;\n\n constructor(client: IDataverseClient, onProgress?: (current: number, total: number) => void) {\n this.client = client;\n this.onProgress = onProgress;\n }\n\n /**\n * Get BPFs by their IDs\n * @param workflowIds Array of workflow IDs\n * @returns Array of Business Process Flows\n */\n async getBusinessProcessFlowsByIds(workflowIds: string[]): Promise<BusinessProcessFlow[]> {\n if (workflowIds.length === 0) {\n return [];\n }\n\n try {\n const batchSize = 20;\n const allResults: RawBusinessProcessFlow[] = [];\n\n for (let i = 0; i < workflowIds.length; i += batchSize) {\n const batch = workflowIds.slice(i, i + batchSize);\n const filterClauses = batch.map((id) => {\n const cleanGuid = id.replace(/[{}]/g, '');\n return `workflowid eq ${cleanGuid}`;\n });\n const filter = `(${filterClauses.join(' or ')}) and category eq 4`;\n\n const result = await this.client.query<RawBusinessProcessFlow>('workflows', {\n select: [\n 'workflowid',\n 'name',\n 'description',\n 'category',\n 'uniquename',\n 'primaryentity',\n 'statecode',\n 'xaml',\n 'ismanaged',\n 'createdon',\n 'modifiedon',\n '_ownerid_value',\n ],\n filter,\n orderBy: ['primaryentity asc', 'name asc'],\n });\n\n allResults.push(...result.value);\n\n // Report progress after each batch\n if (this.onProgress) {\n this.onProgress(allResults.length, workflowIds.length);\n }\n }\n\n // Map to BusinessProcessFlow objects\n return allResults.map((raw) => this.mapToBusinessProcessFlow(raw));\n } catch (error) {\n throw new Error(\n `Failed to retrieve Business Process Flows: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n }\n\n /**\n * Map raw data to BusinessProcessFlow\n */\n private mapToBusinessProcessFlow(raw: RawBusinessProcessFlow): BusinessProcessFlow {\n const definition = this.parseXaml(raw.xaml);\n\n return {\n id: raw.workflowid,\n name: raw.name,\n description: raw.description,\n primaryEntity: raw.primaryentity,\n primaryEntityDisplayName:\n raw['_primaryentity_value@OData.Community.Display.V1.FormattedValue'] || null,\n state: this.getStateName(raw.statecode),\n stateCode: raw.statecode,\n isManaged: raw.ismanaged,\n uniqueName: raw.uniquename,\n xaml: raw.xaml,\n definition,\n owner: raw['_ownerid_value@OData.Community.Display.V1.FormattedValue'] || 'Unknown',\n ownerId: raw._ownerid_value || '',\n modifiedBy: raw['_modifiedby_value@OData.Community.Display.V1.FormattedValue'] || 'Unknown',\n modifiedOn: raw.modifiedon,\n createdOn: raw.createdon,\n };\n }\n\n /**\n * Parse XAML to extract BPF definition\n */\n private parseXaml(xaml: string): {\n stages: Array<{\n id: string;\n name: string;\n entity: string;\n order: number;\n steps: Array<{\n id: string;\n name: string;\n fieldName: string;\n required: boolean;\n order: number;\n }>;\n }>;\n entities: string[];\n totalSteps: number;\n crossEntityFlow: boolean;\n parseError?: string;\n } {\n const stages: Array<{\n id: string;\n name: string;\n entity: string;\n order: number;\n steps: Array<{\n id: string;\n name: string;\n fieldName: string;\n required: boolean;\n order: number;\n }>;\n }> = [];\n\n try {\n // Extract stage information from XAML\n // Pattern: <mxswa:Stage ... EntityName=\"entityname\" StageName=\"stagename\" ...>\n const stageRegex =\n /<mxswa:Stage[^>]*EntityName=\"([^\"]+)\"[^>]*StageName=\"([^\"]+)\"[^>]*StageId=\"([^\"]+)\"[^>]*>/gi;\n let stageMatch;\n let order = 0;\n\n while ((stageMatch = stageRegex.exec(xaml)) !== null) {\n const entity = stageMatch[1];\n const name = stageMatch[2];\n const id = stageMatch[3];\n\n stages.push({\n id,\n name,\n entity,\n order: order++,\n steps: [], // Would need more complex parsing to extract steps\n });\n }\n\n // Extract unique entities\n const entities = [...new Set(stages.map((s) => s.entity))];\n\n // Calculate total steps (would need to parse step elements)\n const stepRegex = /<mxswa:Step[^>]*>/gi;\n const stepMatches = xaml.match(stepRegex);\n const totalSteps = stepMatches ? stepMatches.length : 0;\n\n return {\n stages,\n entities,\n totalSteps,\n crossEntityFlow: entities.length > 1,\n };\n } catch (error) {\n return {\n stages: [],\n entities: [],\n totalSteps: 0,\n crossEntityFlow: false,\n parseError: error instanceof Error ? error.message : 'Failed to parse XAML',\n };\n }\n }\n\n /**\n * Get state name\n */\n private getStateName(statecode: number): 'Draft' | 'Active' {\n switch (statecode) {\n case 0:\n return 'Draft';\n case 1:\n return 'Active';\n default:\n return 'Draft';\n }\n }\n}\n"],"names":["BusinessProcessFlowDiscovery","client","onProgress","__publicField","workflowIds","allResults","i","filter","id","result","raw","error","definition","xaml","stages","stageRegex","stageMatch","order","entity","name","entities","s","stepRegex","stepMatches","totalSteps","statecode"],"mappings":"oKA2BO,MAAMA,CAA6B,CAIxC,YAAYC,EAA0BC,EAAuD,CAH5EC,EAAA,eACTA,EAAA,mBAGN,KAAK,OAASF,EACd,KAAK,WAAaC,CACpB,CAOA,MAAM,6BAA6BE,EAAuD,CACxF,GAAIA,EAAY,SAAW,EACzB,MAAO,CAAA,EAGT,GAAI,CAEF,MAAMC,EAAuC,CAAA,EAE7C,QAASC,EAAI,EAAGA,EAAIF,EAAY,OAAQE,GAAK,GAAW,CAMtD,MAAMC,EAAS,IALDH,EAAY,MAAME,EAAGA,EAAI,EAAS,EACpB,IAAKE,GAExB,iBADWA,EAAG,QAAQ,QAAS,EAAE,CACP,EAClC,EACgC,KAAK,MAAM,CAAC,sBAEvCC,EAAS,MAAM,KAAK,OAAO,MAA8B,YAAa,CAC1E,OAAQ,CACN,aACA,OACA,cACA,WACA,aACA,gBACA,YACA,OACA,YACA,YACA,aACA,gBAAA,EAEF,OAAAF,EACA,QAAS,CAAC,oBAAqB,UAAU,CAAA,CAC1C,EAEDF,EAAW,KAAK,GAAGI,EAAO,KAAK,EAG3B,KAAK,YACP,KAAK,WAAWJ,EAAW,OAAQD,EAAY,MAAM,CAEzD,CAGA,OAAOC,EAAW,IAAKK,GAAQ,KAAK,yBAAyBA,CAAG,CAAC,CACnE,OAASC,EAAO,CACd,MAAM,IAAI,MACR,8CAA8CA,aAAiB,MAAQA,EAAM,QAAU,eAAe,EAAA,CAE1G,CACF,CAKQ,yBAAyBD,EAAkD,CACjF,MAAME,EAAa,KAAK,UAAUF,EAAI,IAAI,EAE1C,MAAO,CACL,GAAIA,EAAI,WACR,KAAMA,EAAI,KACV,YAAaA,EAAI,YACjB,cAAeA,EAAI,cACnB,yBACEA,EAAI,gEAAgE,GAAK,KAC3E,MAAO,KAAK,aAAaA,EAAI,SAAS,EACtC,UAAWA,EAAI,UACf,UAAWA,EAAI,UACf,WAAYA,EAAI,WAChB,KAAMA,EAAI,KACV,WAAAE,EACA,MAAOF,EAAI,0DAA0D,GAAK,UAC1E,QAASA,EAAI,gBAAkB,GAC/B,WAAYA,EAAI,6DAA6D,GAAK,UAClF,WAAYA,EAAI,WAChB,UAAWA,EAAI,SAAA,CAEnB,CAKQ,UAAUG,EAkBhB,CACA,MAAMC,EAYD,CAAA,EAEL,GAAI,CAGF,MAAMC,EACJ,8FACF,IAAIC,EACAC,EAAQ,EAEZ,MAAQD,EAAaD,EAAW,KAAKF,CAAI,KAAO,MAAM,CACpD,MAAMK,EAASF,EAAW,CAAC,EACrBG,EAAOH,EAAW,CAAC,EACnBR,EAAKQ,EAAW,CAAC,EAEvBF,EAAO,KAAK,CACV,GAAAN,EACA,KAAAW,EACA,OAAAD,EACA,MAAOD,IACP,MAAO,CAAA,CAAC,CACT,CACH,CAGA,MAAMG,EAAW,CAAC,GAAG,IAAI,IAAIN,EAAO,IAAKO,GAAMA,EAAE,MAAM,CAAC,CAAC,EAGnDC,EAAY,sBACZC,EAAcV,EAAK,MAAMS,CAAS,EAClCE,EAAaD,EAAcA,EAAY,OAAS,EAEtD,MAAO,CACL,OAAAT,EACA,SAAAM,EACA,WAAAI,EACA,gBAAiBJ,EAAS,OAAS,CAAA,CAEvC,OAAST,EAAO,CACd,MAAO,CACL,OAAQ,CAAA,EACR,SAAU,CAAA,EACV,WAAY,EACZ,gBAAiB,GACjB,WAAYA,aAAiB,MAAQA,EAAM,QAAU,sBAAA,CAEzD,CACF,CAKQ,aAAac,EAAuC,CAC1D,OAAQA,EAAA,CACN,IAAK,GACH,MAAO,QACT,IAAK,GACH,MAAO,SACT,QACE,MAAO,OAAA,CAEb,CACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
class r{constructor(e){this.client=e}async getAttributeMaskingRules(){return(await this.client.query("attributemaskingrules",{select:["attributemaskingruleid","entitylogicalname","attributelogicalname","maskingtype","maskingformat","description","ismanaged"],orderBy:["entitylogicalname","attributelogicalname"]})).value}async getColumnSecurityProfiles(){return(await this.client.query("columnsecurityprofiles",{select:["columnsecurityprofileid","name","description","ismanaged","organizationid"],orderBy:["name"]})).value}async getColumnPermissions(e){const t=e.replace(/[{}]/g,"");return(await this.client.query("columnpermissions",{select:["columnpermissionid","columnsecurityprofileid","entitylogicalname","attributelogicalname","cancreate","canread","canupdate"],filter:`columnsecurityprofileid eq ${t}`,orderBy:["entitylogicalname","attributelogicalname"]})).value}getMaskingTypeDisplayName(e){switch(e){case 1:return"Full";case 2:return"Partial";case 3:return"Email";case 4:return"Custom";default:return"Unknown"}}}export{r as ColumnSecurityDiscovery};
|
|
2
|
+
//# sourceMappingURL=ColumnSecurityDiscovery-B3yVn0LZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ColumnSecurityDiscovery-B3yVn0LZ.js","sources":["../../src/core/discovery/ColumnSecurityDiscovery.ts"],"sourcesContent":["import type { IDataverseClient } from '../dataverse/IDataverseClient.js';\n\n/**\n * Attribute Masking Rule\n */\nexport interface AttributeMaskingRule {\n attributemaskingruleid: string;\n name?: string; // Optional - may not exist in all Dataverse versions\n entitylogicalname: string;\n attributelogicalname: string;\n maskingtype: number; // 1 = Full, 2 = Partial, 3 = Email, 4 = Custom\n maskingformat?: string;\n description?: string;\n ismanaged: boolean;\n}\n\n/**\n * Column Level Security Profile (Security Masking Rule)\n */\nexport interface ColumnSecurityProfile {\n columnsecurityprofileid: string;\n name: string;\n description?: string;\n ismanaged: boolean;\n organizationid: string;\n}\n\n/**\n * Column Permission (which columns this profile can access)\n */\nexport interface ColumnPermission {\n columnpermissionid: string;\n columnsecurityprofileid: string;\n entitylogicalname: string;\n attributelogicalname: string;\n cancreate: boolean;\n canread: boolean;\n canupdate: boolean;\n}\n\n/**\n * Discovery class for column-level security components\n */\nexport class ColumnSecurityDiscovery {\n constructor(\n private client: IDataverseClient\n ) {}\n\n /**\n * Get all attribute masking rules\n */\n async getAttributeMaskingRules(): Promise<AttributeMaskingRule[]> {\n const result = await this.client.query<AttributeMaskingRule>('attributemaskingrules', {\n select: [\n 'attributemaskingruleid',\n 'entitylogicalname',\n 'attributelogicalname',\n 'maskingtype',\n 'maskingformat',\n 'description',\n 'ismanaged',\n ],\n orderBy: ['entitylogicalname', 'attributelogicalname'],\n });\n\n return result.value;\n }\n\n /**\n * Get column security profiles (security masking rules)\n */\n async getColumnSecurityProfiles(): Promise<ColumnSecurityProfile[]> {\n const result = await this.client.query<ColumnSecurityProfile>('columnsecurityprofiles', {\n select: [\n 'columnsecurityprofileid',\n 'name',\n 'description',\n 'ismanaged',\n 'organizationid',\n ],\n orderBy: ['name'],\n });\n\n return result.value;\n }\n\n /**\n * Get column permissions for a specific profile\n */\n async getColumnPermissions(profileId: string): Promise<ColumnPermission[]> {\n const cleanId = profileId.replace(/[{}]/g, '');\n\n const result = await this.client.query<ColumnPermission>('columnpermissions', {\n select: [\n 'columnpermissionid',\n 'columnsecurityprofileid',\n 'entitylogicalname',\n 'attributelogicalname',\n 'cancreate',\n 'canread',\n 'canupdate',\n ],\n filter: `columnsecurityprofileid eq ${cleanId}`,\n orderBy: ['entitylogicalname', 'attributelogicalname'],\n });\n\n return result.value;\n }\n\n /**\n * Get masking type display name\n */\n getMaskingTypeDisplayName(maskingType: number): string {\n switch (maskingType) {\n case 1:\n return 'Full';\n case 2:\n return 'Partial';\n case 3:\n return 'Email';\n case 4:\n return 'Custom';\n default:\n return 'Unknown';\n }\n }\n}\n"],"names":["ColumnSecurityDiscovery","client","profileId","cleanId","maskingType"],"mappings":"AA2CO,MAAMA,CAAwB,CACnC,YACUC,EACR,CADQ,KAAA,OAAAA,CACP,CAKH,MAAM,0BAA4D,CAchE,OAbe,MAAM,KAAK,OAAO,MAA4B,wBAAyB,CACpF,OAAQ,CACN,yBACA,oBACA,uBACA,cACA,gBACA,cACA,WAAA,EAEF,QAAS,CAAC,oBAAqB,sBAAsB,CAAA,CACtD,GAEa,KAChB,CAKA,MAAM,2BAA8D,CAYlE,OAXe,MAAM,KAAK,OAAO,MAA6B,yBAA0B,CACtF,OAAQ,CACN,0BACA,OACA,cACA,YACA,gBAAA,EAEF,QAAS,CAAC,MAAM,CAAA,CACjB,GAEa,KAChB,CAKA,MAAM,qBAAqBC,EAAgD,CACzE,MAAMC,EAAUD,EAAU,QAAQ,QAAS,EAAE,EAgB7C,OAde,MAAM,KAAK,OAAO,MAAwB,oBAAqB,CAC5E,OAAQ,CACN,qBACA,0BACA,oBACA,uBACA,YACA,UACA,WAAA,EAEF,OAAQ,8BAA8BC,CAAO,GAC7C,QAAS,CAAC,oBAAqB,sBAAsB,CAAA,CACtD,GAEa,KAChB,CAKA,0BAA0BC,EAA6B,CACrD,OAAQA,EAAA,CACN,IAAK,GACH,MAAO,OACT,IAAK,GACH,MAAO,UACT,IAAK,GACH,MAAO,QACT,IAAK,GACH,MAAO,SACT,QACE,MAAO,SAAA,CAEb,CACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var l=Object.defineProperty;var d=(o,e,n)=>e in o?l(o,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):o[e]=n;var i=(o,e,n)=>d(o,typeof e!="symbol"?e+"":e,n);class h{constructor(e,n){i(this,"client");i(this,"onProgress");this.client=e,this.onProgress=n}async getConnectionReferencesByIds(e){if(e.length===0)return[];try{const c=[];for(let t=0;t<e.length;t+=20){const r=e.slice(t,t+20).map(a=>`connectionreferenceid eq ${a.replace(/[{}]/g,"")}`).join(" or "),s=await this.client.query("connectionreferences",{select:["connectionreferenceid","connectionreferencelogicalname","connectionreferencedisplayname","description","connectionid","connectorid","ismanaged","iscustomizable","statecode","statuscode","createdon","modifiedon","_ownerid_value"],filter:r,orderBy:["connectionreferencelogicalname asc"]});c.push(...s.value),this.onProgress&&this.onProgress(c.length,e.length)}return c.map(t=>this.mapToConnectionReference(t))}catch(n){throw new Error(`Failed to retrieve Connection References: ${n instanceof Error?n.message:"Unknown error"}`)}}mapToConnectionReference(e){var n;return{id:e.connectionreferenceid,name:e.connectionreferencelogicalname,displayName:e.connectionreferencedisplayname||e.connectionreferencelogicalname,description:e.description,connectionId:e.connectionid,connectorId:e.connectorid,connectorDisplayName:e.connectorid?this.getConnectorDisplayName(e.connectorid):null,isManaged:e.ismanaged,isCustomizable:((n=e.iscustomizable)==null?void 0:n.Value)??!0,statecode:e.statecode,statuscode:e.statuscode,owner:e["_ownerid_value@OData.Community.Display.V1.FormattedValue"]||"Unknown",ownerId:e._ownerid_value||"",modifiedBy:e["_modifiedby_value@OData.Community.Display.V1.FormattedValue"]||"Unknown",modifiedOn:e.modifiedon,createdOn:e.createdon}}getConnectorDisplayName(e){const n=e.match(/\/apis\/([^/]+)/);return n?n[1]:e}}export{h as ConnectionReferenceDiscovery};
|
|
2
|
+
//# sourceMappingURL=ConnectionReferenceDiscovery-DFTQuo-S.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConnectionReferenceDiscovery-DFTQuo-S.js","sources":["../../src/core/discovery/ConnectionReferenceDiscovery.ts"],"sourcesContent":["import type { IDataverseClient } from '../dataverse/IDataverseClient.js';\nimport type { ConnectionReference } from '../types/connectionReference.js';\n\ninterface RawConnectionReference {\n connectionreferenceid: string;\n connectionreferencelogicalname: string;\n connectionreferencedisplayname: string | null;\n description: string | null;\n connectionid: string | null;\n connectorid: string | null;\n ismanaged: boolean;\n iscustomizable: { Value: boolean };\n statecode: number;\n statuscode: number;\n createdon: string;\n modifiedon: string;\n _ownerid_value?: string;\n '_ownerid_value@OData.Community.Display.V1.FormattedValue'?: string;\n '_modifiedby_value@OData.Community.Display.V1.FormattedValue'?: string;\n}\n\nexport class ConnectionReferenceDiscovery {\n private readonly client: IDataverseClient;\n private onProgress?: (current: number, total: number) => void;\n\n constructor(client: IDataverseClient, onProgress?: (current: number, total: number) => void) {\n this.client = client;\n this.onProgress = onProgress;\n }\n\n async getConnectionReferencesByIds(ids: string[]): Promise<ConnectionReference[]> {\n if (ids.length === 0) return [];\n\n try {\n const batchSize = 20;\n const allResults: RawConnectionReference[] = [];\n\n for (let i = 0; i < ids.length; i += batchSize) {\n const batch = ids.slice(i, i + batchSize);\n const filterClauses = batch.map(id => `connectionreferenceid eq ${id.replace(/[{}]/g, '')}`);\n const filter = filterClauses.join(' or ');\n\n const result = await this.client.query<RawConnectionReference>('connectionreferences', {\n select: ['connectionreferenceid', 'connectionreferencelogicalname', 'connectionreferencedisplayname',\n 'description', 'connectionid', 'connectorid', 'ismanaged', 'iscustomizable',\n 'statecode', 'statuscode', 'createdon', 'modifiedon', '_ownerid_value'],\n filter,\n orderBy: ['connectionreferencelogicalname asc'],\n });\n\n allResults.push(...result.value);\n\n // Report progress after each batch\n if (this.onProgress) {\n this.onProgress(allResults.length, ids.length);\n }\n }\n\n return allResults.map(raw => this.mapToConnectionReference(raw));\n } catch (error) {\n throw new Error(`Failed to retrieve Connection References: ${error instanceof Error ? error.message : 'Unknown error'}`);\n }\n }\n\n private mapToConnectionReference(raw: RawConnectionReference): ConnectionReference {\n return {\n id: raw.connectionreferenceid,\n name: raw.connectionreferencelogicalname,\n displayName: raw.connectionreferencedisplayname || raw.connectionreferencelogicalname,\n description: raw.description,\n connectionId: raw.connectionid,\n connectorId: raw.connectorid,\n connectorDisplayName: raw.connectorid ? this.getConnectorDisplayName(raw.connectorid) : null,\n isManaged: raw.ismanaged,\n isCustomizable: raw.iscustomizable?.Value ?? true,\n statecode: raw.statecode,\n statuscode: raw.statuscode,\n owner: raw['_ownerid_value@OData.Community.Display.V1.FormattedValue'] || 'Unknown',\n ownerId: raw._ownerid_value || '',\n modifiedBy: raw['_modifiedby_value@OData.Community.Display.V1.FormattedValue'] || 'Unknown',\n modifiedOn: raw.modifiedon,\n createdOn: raw.createdon,\n };\n }\n\n private getConnectorDisplayName(connectorId: string): string {\n // Extract connector name from connector ID (format: /providers/Microsoft.PowerApps/apis/{name})\n const match = connectorId.match(/\\/apis\\/([^/]+)/);\n return match ? match[1] : connectorId;\n }\n}\n"],"names":["ConnectionReferenceDiscovery","client","onProgress","__publicField","ids","allResults","i","filter","id","result","raw","error","_a","connectorId","match"],"mappings":"oKAqBO,MAAMA,CAA6B,CAIxC,YAAYC,EAA0BC,EAAuD,CAH5EC,EAAA,eACTA,EAAA,mBAGN,KAAK,OAASF,EACd,KAAK,WAAaC,CACpB,CAEA,MAAM,6BAA6BE,EAA+C,CAChF,GAAIA,EAAI,SAAW,EAAG,MAAO,CAAA,EAE7B,GAAI,CAEF,MAAMC,EAAuC,CAAA,EAE7C,QAASC,EAAI,EAAGA,EAAIF,EAAI,OAAQE,GAAK,GAAW,CAG9C,MAAMC,EAFQH,EAAI,MAAME,EAAGA,EAAI,EAAS,EACZ,IAAIE,GAAM,4BAA4BA,EAAG,QAAQ,QAAS,EAAE,CAAC,EAAE,EAC9D,KAAK,MAAM,EAElCC,EAAS,MAAM,KAAK,OAAO,MAA8B,uBAAwB,CACrF,OAAQ,CAAC,wBAAyB,iCAAkC,iCAClE,cAAe,eAAgB,cAAe,YAAa,iBAC3D,YAAa,aAAc,YAAa,aAAc,gBAAA,EACxD,OAAAF,EACA,QAAS,CAAC,oCAAoC,CAAA,CAC/C,EAEDF,EAAW,KAAK,GAAGI,EAAO,KAAK,EAG3B,KAAK,YACP,KAAK,WAAWJ,EAAW,OAAQD,EAAI,MAAM,CAEjD,CAEA,OAAOC,EAAW,IAAIK,GAAO,KAAK,yBAAyBA,CAAG,CAAC,CACjE,OAASC,EAAO,CACd,MAAM,IAAI,MAAM,6CAA6CA,aAAiB,MAAQA,EAAM,QAAU,eAAe,EAAE,CACzH,CACF,CAEQ,yBAAyBD,EAAkD,CA3C9E,IAAAE,EA4CH,MAAO,CACL,GAAIF,EAAI,sBACR,KAAMA,EAAI,+BACV,YAAaA,EAAI,gCAAkCA,EAAI,+BACvD,YAAaA,EAAI,YACjB,aAAcA,EAAI,aAClB,YAAaA,EAAI,YACjB,qBAAsBA,EAAI,YAAc,KAAK,wBAAwBA,EAAI,WAAW,EAAI,KACxF,UAAWA,EAAI,UACf,iBAAgBE,EAAAF,EAAI,iBAAJ,YAAAE,EAAoB,QAAS,GAC7C,UAAWF,EAAI,UACf,WAAYA,EAAI,WAChB,MAAOA,EAAI,0DAA0D,GAAK,UAC1E,QAASA,EAAI,gBAAkB,GAC/B,WAAYA,EAAI,6DAA6D,GAAK,UAClF,WAAYA,EAAI,WAChB,UAAWA,EAAI,SAAA,CAEnB,CAEQ,wBAAwBG,EAA6B,CAE3D,MAAMC,EAAQD,EAAY,MAAM,iBAAiB,EACjD,OAAOC,EAAQA,EAAM,CAAC,EAAID,CAC5B,CACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var m=Object.defineProperty;var d=(r,e,t)=>e in r?m(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t;var s=(r,e,t)=>d(r,typeof e!="symbol"?e+"":e,t);class h{constructor(e,t){s(this,"client");s(this,"onProgress");this.client=e,this.onProgress=t}async getCustomAPIsByIds(e){if(e.length===0)return[];try{const i=[];for(let n=0;n<e.length;n+=20){const c=e.slice(n,n+20).map(p=>`customapiid eq ${p.replace(/[{}]/g,"")}`).join(" or "),l=await this.client.query("customapis",{select:["customapiid","uniquename","displayname","description","bindingtype","boundentitylogicalname","isfunction","isprivate","ismanaged","allowedcustomprocessingsteptype","executeprivilegename","createdon","modifiedon","_ownerid_value"],filter:c,orderBy:["uniquename asc"]});i.push(...l.value),this.onProgress&&this.onProgress(i.length,e.length)}const a=[];for(const n of i){const[o,u]=await Promise.all([this.getRequestParameters(n.customapiid),this.getResponseProperties(n.customapiid)]);a.push(this.mapToCustomAPI(n,o,u))}return a}catch(t){throw new Error(`Failed to retrieve Custom APIs: ${t instanceof Error?t.message:"Unknown error"}`)}}async getRequestParameters(e){try{return(await this.client.query("customapirequestparameters",{select:["customapirequestparameterid","uniquename","displayname","description","type","isoptional","logicalentityname"],filter:`_customapiid_value eq ${e.replace(/[{}]/g,"")}`,orderBy:["uniquename asc"]})).value.map(i=>this.mapToParameter(i,!0))}catch{return[]}}async getResponseProperties(e){try{return(await this.client.query("customapiresponseproperties",{select:["customapiresponsepropertyid","uniquename","displayname","description","type","isoptional","logicalentityname"],filter:`_customapiid_value eq ${e.replace(/[{}]/g,"")}`,orderBy:["uniquename asc"]})).value.map(i=>this.mapToParameter(i,!1))}catch{return[]}}mapToCustomAPI(e,t,i){return{id:e.customapiid,uniqueName:e.uniquename,displayName:e.displayname||e.uniquename,description:e.description,bindingType:this.getBindingType(e.bindingtype),boundEntityLogicalName:e.boundentitylogicalname,isFunction:e.isfunction,isPrivate:e.isprivate,isManaged:e.ismanaged,allowedCustomProcessingStepType:this.getAllowedStepType(e.allowedcustomprocessingsteptype),executionPrivilege:this.getExecutionPrivilege(e.executeprivilegename),requestParameters:t,responseProperties:i,owner:e["_ownerid_value@OData.Community.Display.V1.FormattedValue"]||"Unknown",ownerId:e._ownerid_value||"",modifiedBy:e["_modifiedby_value@OData.Community.Display.V1.FormattedValue"]||"Unknown",modifiedOn:e.modifiedon,createdOn:e.createdon}}mapToParameter(e,t){return{id:(t?e.customapirequestparameterid:e.customapiresponsepropertyid)||"",uniqueName:e.uniquename,displayName:e.displayname||e.uniquename,description:e.description,type:this.getParameterType(e.type),typeName:this.getParameterTypeName(e.type),isOptional:e.isoptional,logicalEntityName:e.logicalentityname}}getBindingType(e){switch(e){case 0:return"Global";case 1:return"Entity";case 2:return"EntityCollection";default:return"Global"}}getAllowedStepType(e){switch(e){case 0:return"None";case 1:return"AsyncOnly";case 2:return"SyncAndAsync";default:return"None"}}getExecutionPrivilege(e){return e?e.includes("Basic")?"Basic":e.includes("Local")?"Local":e.includes("Deep")?"Deep":e.includes("Global")?"Global":"None":"None"}getParameterType(e){switch(e){case 0:return"Boolean";case 1:return"DateTime";case 2:return"Decimal";case 3:return"Entity";case 4:return"EntityCollection";case 5:return"EntityReference";case 6:return"Float";case 7:return"Integer";case 8:return"Money";case 9:return"Picklist";case 10:return"String";case 11:return"StringArray";case 12:return"Guid";default:return"String"}}getParameterTypeName(e){return this.getParameterType(e)}}export{h as CustomAPIDiscovery};
|
|
2
|
+
//# sourceMappingURL=CustomAPIDiscovery-BiQS_ENt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CustomAPIDiscovery-BiQS_ENt.js","sources":["../../src/core/discovery/CustomAPIDiscovery.ts"],"sourcesContent":["import type { IDataverseClient } from '../dataverse/IDataverseClient.js';\nimport type { CustomAPI, CustomAPIParameter } from '../types/customApi.js';\n\n/**\n * Raw Custom API data from Dataverse\n */\ninterface RawCustomAPI {\n customapiid: string;\n uniquename: string;\n displayname: string | null;\n description: string | null;\n bindingtype: number;\n boundentitylogicalname: string | null;\n isfunction: boolean;\n isprivate: boolean;\n ismanaged: boolean;\n allowedcustomprocessingsteptype: number;\n executeprivilegename: string | null;\n createdon: string;\n modifiedon: string;\n _ownerid_value?: string;\n '_ownerid_value@OData.Community.Display.V1.FormattedValue'?: string;\n '_modifiedby_value@OData.Community.Display.V1.FormattedValue'?: string;\n}\n\n/**\n * Raw Custom API parameter data\n */\ninterface RawCustomAPIParameter {\n customapirequestparameterid?: string;\n customapiresponsepropertyid?: string;\n uniquename: string;\n displayname: string | null;\n description: string | null;\n type: number;\n isoptional: boolean;\n logicalentityname: string | null;\n}\n\n/**\n * Discovers Custom APIs\n */\nexport class CustomAPIDiscovery {\n private readonly client: IDataverseClient;\n private onProgress?: (current: number, total: number) => void;\n\n constructor(client: IDataverseClient, onProgress?: (current: number, total: number) => void) {\n this.client = client;\n this.onProgress = onProgress;\n }\n\n /**\n * Get all Custom APIs in the solution\n * @param customApiIds Array of custom API IDs from solution components\n * @returns Array of Custom APIs with parameters\n */\n async getCustomAPIsByIds(customApiIds: string[]): Promise<CustomAPI[]> {\n if (customApiIds.length === 0) {\n return [];\n }\n\n try {\n const batchSize = 20;\n const allResults: RawCustomAPI[] = [];\n\n for (let i = 0; i < customApiIds.length; i += batchSize) {\n const batch = customApiIds.slice(i, i + batchSize);\n const filterClauses = batch.map((id) => {\n const cleanGuid = id.replace(/[{}]/g, '');\n return `customapiid eq ${cleanGuid}`;\n });\n const filter = filterClauses.join(' or ');\n\n const result = await this.client.query<RawCustomAPI>('customapis', {\n select: [\n 'customapiid',\n 'uniquename',\n 'displayname',\n 'description',\n 'bindingtype',\n 'boundentitylogicalname',\n 'isfunction',\n 'isprivate',\n 'ismanaged',\n 'allowedcustomprocessingsteptype',\n 'executeprivilegename',\n 'createdon',\n 'modifiedon',\n '_ownerid_value',\n ],\n filter,\n orderBy: ['uniquename asc'],\n });\n\n allResults.push(...result.value);\n\n // Report progress after each batch\n if (this.onProgress) {\n this.onProgress(allResults.length, customApiIds.length);\n }\n }\n\n // For each Custom API, fetch its request parameters and response properties\n const customAPIs: CustomAPI[] = [];\n for (const rawApi of allResults) {\n const [requestParams, responseProps] = await Promise.all([\n this.getRequestParameters(rawApi.customapiid),\n this.getResponseProperties(rawApi.customapiid),\n ]);\n\n customAPIs.push(this.mapToCustomAPI(rawApi, requestParams, responseProps));\n }\n\n return customAPIs;\n } catch (error) {\n throw new Error(\n `Failed to retrieve Custom APIs: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n }\n\n /**\n * Get request parameters for a Custom API\n */\n private async getRequestParameters(customApiId: string): Promise<CustomAPIParameter[]> {\n try {\n const result = await this.client.query<RawCustomAPIParameter>(\n 'customapirequestparameters',\n {\n select: [\n 'customapirequestparameterid',\n 'uniquename',\n 'displayname',\n 'description',\n 'type',\n 'isoptional',\n 'logicalentityname',\n ],\n filter: `_customapiid_value eq ${customApiId.replace(/[{}]/g, '')}`,\n orderBy: ['uniquename asc'],\n }\n );\n\n return result.value.map((raw) => this.mapToParameter(raw, true));\n } catch (error) {\n return [];\n }\n }\n\n /**\n * Get response properties for a Custom API\n */\n private async getResponseProperties(customApiId: string): Promise<CustomAPIParameter[]> {\n try {\n const result = await this.client.query<RawCustomAPIParameter>(\n 'customapiresponseproperties',\n {\n select: [\n 'customapiresponsepropertyid',\n 'uniquename',\n 'displayname',\n 'description',\n 'type',\n 'isoptional',\n 'logicalentityname',\n ],\n filter: `_customapiid_value eq ${customApiId.replace(/[{}]/g, '')}`,\n orderBy: ['uniquename asc'],\n }\n );\n\n return result.value.map((raw) => this.mapToParameter(raw, false));\n } catch (error) {\n return [];\n }\n }\n\n /**\n * Map raw data to CustomAPI\n */\n private mapToCustomAPI(\n raw: RawCustomAPI,\n requestParameters: CustomAPIParameter[],\n responseProperties: CustomAPIParameter[]\n ): CustomAPI {\n return {\n id: raw.customapiid,\n uniqueName: raw.uniquename,\n displayName: raw.displayname || raw.uniquename,\n description: raw.description,\n bindingType: this.getBindingType(raw.bindingtype),\n boundEntityLogicalName: raw.boundentitylogicalname,\n isFunction: raw.isfunction,\n isPrivate: raw.isprivate,\n isManaged: raw.ismanaged,\n allowedCustomProcessingStepType: this.getAllowedStepType(\n raw.allowedcustomprocessingsteptype\n ),\n executionPrivilege: this.getExecutionPrivilege(raw.executeprivilegename),\n requestParameters,\n responseProperties,\n owner: raw['_ownerid_value@OData.Community.Display.V1.FormattedValue'] || 'Unknown',\n ownerId: raw._ownerid_value || '',\n modifiedBy: raw['_modifiedby_value@OData.Community.Display.V1.FormattedValue'] || 'Unknown',\n modifiedOn: raw.modifiedon,\n createdOn: raw.createdon,\n };\n }\n\n /**\n * Map raw parameter to CustomAPIParameter\n */\n private mapToParameter(raw: RawCustomAPIParameter, isRequest: boolean): CustomAPIParameter {\n return {\n id: (isRequest ? raw.customapirequestparameterid : raw.customapiresponsepropertyid) || '',\n uniqueName: raw.uniquename,\n displayName: raw.displayname || raw.uniquename,\n description: raw.description,\n type: this.getParameterType(raw.type),\n typeName: this.getParameterTypeName(raw.type),\n isOptional: raw.isoptional,\n logicalEntityName: raw.logicalentityname,\n };\n }\n\n /**\n * Get binding type name\n */\n private getBindingType(bindingType: number): 'Global' | 'Entity' | 'EntityCollection' {\n switch (bindingType) {\n case 0:\n return 'Global';\n case 1:\n return 'Entity';\n case 2:\n return 'EntityCollection';\n default:\n return 'Global';\n }\n }\n\n /**\n * Get allowed custom processing step type\n */\n private getAllowedStepType(type: number): 'None' | 'AsyncOnly' | 'SyncAndAsync' {\n switch (type) {\n case 0:\n return 'None';\n case 1:\n return 'AsyncOnly';\n case 2:\n return 'SyncAndAsync';\n default:\n return 'None';\n }\n }\n\n /**\n * Get execution privilege\n */\n private getExecutionPrivilege(\n privilegeName: string | null\n ): 'None' | 'Basic' | 'Local' | 'Deep' | 'Global' {\n if (!privilegeName) return 'None';\n\n // Privilege names typically end with prvRead{EntityName}, prvWrite{EntityName}, etc.\n if (privilegeName.includes('Basic')) return 'Basic';\n if (privilegeName.includes('Local')) return 'Local';\n if (privilegeName.includes('Deep')) return 'Deep';\n if (privilegeName.includes('Global')) return 'Global';\n\n return 'None';\n }\n\n /**\n * Get parameter type\n */\n private getParameterType(type: number):\n | 'Boolean'\n | 'DateTime'\n | 'Decimal'\n | 'Entity'\n | 'EntityCollection'\n | 'EntityReference'\n | 'Float'\n | 'Integer'\n | 'Money'\n | 'Picklist'\n | 'String'\n | 'StringArray'\n | 'Guid' {\n switch (type) {\n case 0:\n return 'Boolean';\n case 1:\n return 'DateTime';\n case 2:\n return 'Decimal';\n case 3:\n return 'Entity';\n case 4:\n return 'EntityCollection';\n case 5:\n return 'EntityReference';\n case 6:\n return 'Float';\n case 7:\n return 'Integer';\n case 8:\n return 'Money';\n case 9:\n return 'Picklist';\n case 10:\n return 'String';\n case 11:\n return 'StringArray';\n case 12:\n return 'Guid';\n default:\n return 'String';\n }\n }\n\n /**\n * Get parameter type name\n */\n private getParameterTypeName(type: number): string {\n const typeMap = this.getParameterType(type);\n return typeMap;\n }\n}\n"],"names":["CustomAPIDiscovery","client","onProgress","__publicField","customApiIds","allResults","i","filter","id","result","customAPIs","rawApi","requestParams","responseProps","error","customApiId","raw","requestParameters","responseProperties","isRequest","bindingType","type","privilegeName"],"mappings":"oKA0CO,MAAMA,CAAmB,CAI9B,YAAYC,EAA0BC,EAAuD,CAH5EC,EAAA,eACTA,EAAA,mBAGN,KAAK,OAASF,EACd,KAAK,WAAaC,CACpB,CAOA,MAAM,mBAAmBE,EAA8C,CACrE,GAAIA,EAAa,SAAW,EAC1B,MAAO,CAAA,EAGT,GAAI,CAEF,MAAMC,EAA6B,CAAA,EAEnC,QAASC,EAAI,EAAGA,EAAIF,EAAa,OAAQE,GAAK,GAAW,CAMvD,MAAMC,EALQH,EAAa,MAAME,EAAGA,EAAI,EAAS,EACrB,IAAKE,GAExB,kBADWA,EAAG,QAAQ,QAAS,EAAE,CACN,EACnC,EAC4B,KAAK,MAAM,EAElCC,EAAS,MAAM,KAAK,OAAO,MAAoB,aAAc,CACjE,OAAQ,CACN,cACA,aACA,cACA,cACA,cACA,yBACA,aACA,YACA,YACA,kCACA,uBACA,YACA,aACA,gBAAA,EAEF,OAAAF,EACA,QAAS,CAAC,gBAAgB,CAAA,CAC3B,EAEDF,EAAW,KAAK,GAAGI,EAAO,KAAK,EAG3B,KAAK,YACP,KAAK,WAAWJ,EAAW,OAAQD,EAAa,MAAM,CAE1D,CAGA,MAAMM,EAA0B,CAAA,EAChC,UAAWC,KAAUN,EAAY,CAC/B,KAAM,CAACO,EAAeC,CAAa,EAAI,MAAM,QAAQ,IAAI,CACvD,KAAK,qBAAqBF,EAAO,WAAW,EAC5C,KAAK,sBAAsBA,EAAO,WAAW,CAAA,CAC9C,EAEDD,EAAW,KAAK,KAAK,eAAeC,EAAQC,EAAeC,CAAa,CAAC,CAC3E,CAEA,OAAOH,CACT,OAASI,EAAO,CACd,MAAM,IAAI,MACR,mCAAmCA,aAAiB,MAAQA,EAAM,QAAU,eAAe,EAAA,CAE/F,CACF,CAKA,MAAc,qBAAqBC,EAAoD,CACrF,GAAI,CAkBF,OAjBe,MAAM,KAAK,OAAO,MAC/B,6BACA,CACE,OAAQ,CACN,8BACA,aACA,cACA,cACA,OACA,aACA,mBAAA,EAEF,OAAQ,yBAAyBA,EAAY,QAAQ,QAAS,EAAE,CAAC,GACjE,QAAS,CAAC,gBAAgB,CAAA,CAC5B,GAGY,MAAM,IAAKC,GAAQ,KAAK,eAAeA,EAAK,EAAI,CAAC,CACjE,MAAgB,CACd,MAAO,CAAA,CACT,CACF,CAKA,MAAc,sBAAsBD,EAAoD,CACtF,GAAI,CAkBF,OAjBe,MAAM,KAAK,OAAO,MAC/B,8BACA,CACE,OAAQ,CACN,8BACA,aACA,cACA,cACA,OACA,aACA,mBAAA,EAEF,OAAQ,yBAAyBA,EAAY,QAAQ,QAAS,EAAE,CAAC,GACjE,QAAS,CAAC,gBAAgB,CAAA,CAC5B,GAGY,MAAM,IAAKC,GAAQ,KAAK,eAAeA,EAAK,EAAK,CAAC,CAClE,MAAgB,CACd,MAAO,CAAA,CACT,CACF,CAKQ,eACNA,EACAC,EACAC,EACW,CACX,MAAO,CACL,GAAIF,EAAI,YACR,WAAYA,EAAI,WAChB,YAAaA,EAAI,aAAeA,EAAI,WACpC,YAAaA,EAAI,YACjB,YAAa,KAAK,eAAeA,EAAI,WAAW,EAChD,uBAAwBA,EAAI,uBAC5B,WAAYA,EAAI,WAChB,UAAWA,EAAI,UACf,UAAWA,EAAI,UACf,gCAAiC,KAAK,mBACpCA,EAAI,+BAAA,EAEN,mBAAoB,KAAK,sBAAsBA,EAAI,oBAAoB,EACvE,kBAAAC,EACA,mBAAAC,EACA,MAAOF,EAAI,0DAA0D,GAAK,UAC1E,QAASA,EAAI,gBAAkB,GAC/B,WAAYA,EAAI,6DAA6D,GAAK,UAClF,WAAYA,EAAI,WAChB,UAAWA,EAAI,SAAA,CAEnB,CAKQ,eAAeA,EAA4BG,EAAwC,CACzF,MAAO,CACL,IAAKA,EAAYH,EAAI,4BAA8BA,EAAI,8BAAgC,GACvF,WAAYA,EAAI,WAChB,YAAaA,EAAI,aAAeA,EAAI,WACpC,YAAaA,EAAI,YACjB,KAAM,KAAK,iBAAiBA,EAAI,IAAI,EACpC,SAAU,KAAK,qBAAqBA,EAAI,IAAI,EAC5C,WAAYA,EAAI,WAChB,kBAAmBA,EAAI,iBAAA,CAE3B,CAKQ,eAAeI,EAA+D,CACpF,OAAQA,EAAA,CACN,IAAK,GACH,MAAO,SACT,IAAK,GACH,MAAO,SACT,IAAK,GACH,MAAO,mBACT,QACE,MAAO,QAAA,CAEb,CAKQ,mBAAmBC,EAAqD,CAC9E,OAAQA,EAAA,CACN,IAAK,GACH,MAAO,OACT,IAAK,GACH,MAAO,YACT,IAAK,GACH,MAAO,eACT,QACE,MAAO,MAAA,CAEb,CAKQ,sBACNC,EACgD,CAChD,OAAKA,EAGDA,EAAc,SAAS,OAAO,EAAU,QACxCA,EAAc,SAAS,OAAO,EAAU,QACxCA,EAAc,SAAS,MAAM,EAAU,OACvCA,EAAc,SAAS,QAAQ,EAAU,SAEtC,OARoB,MAS7B,CAKQ,iBAAiBD,EAad,CACT,OAAQA,EAAA,CACN,IAAK,GACH,MAAO,UACT,IAAK,GACH,MAAO,WACT,IAAK,GACH,MAAO,UACT,IAAK,GACH,MAAO,SACT,IAAK,GACH,MAAO,mBACT,IAAK,GACH,MAAO,kBACT,IAAK,GACH,MAAO,QACT,IAAK,GACH,MAAO,UACT,IAAK,GACH,MAAO,QACT,IAAK,GACH,MAAO,WACT,IAAK,IACH,MAAO,SACT,IAAK,IACH,MAAO,cACT,IAAK,IACH,MAAO,OACT,QACE,MAAO,QAAA,CAEb,CAKQ,qBAAqBA,EAAsB,CAEjD,OADgB,KAAK,iBAAiBA,CAAI,CAE5C,CACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var m=Object.defineProperty;var d=(o,e,t)=>e in o?m(o,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):o[e]=t;var r=(o,e,t)=>d(o,typeof e!="symbol"?e+"":e,t);class y{constructor(e,t){r(this,"client");r(this,"onProgress");this.client=e,this.onProgress=t}async getConnectorsByIds(e){if(e.length===0)return[];try{const s=[];for(let i=0;i<e.length;i+=20){const p=e.slice(i,i+20).map(a=>`connectorid eq ${a.startsWith("{")?a:`{${a}}`}`).join(" or "),l=await this.client.query("connectors",{select:["connectorid","name","displayname","description","connectortype","iconuri","ismanaged","iscustomizable","capabilities","connectionparameters","ownerid","modifiedon","modifiedby","policytemplateinstances","openapidefinition"],filter:p});s.push(...l.value),this.onProgress&&this.onProgress(s.length,e.length)}return s.map(i=>this.mapToCustomConnector(i))}catch{return[]}}mapToCustomConnector(e){var c;const t=[];if(e.capabilities)try{const n=JSON.parse(e.capabilities);Array.isArray(n)?t.push(...n):typeof n=="object"&&t.push(...Object.keys(n))}catch{}const s=[];if(e.connectionparameters)try{const n=JSON.parse(e.connectionparameters);typeof n=="object"&&s.push(...Object.keys(n))}catch{}const i={0:"NotSpecified",1:"Custom",2:"Certified",3:"Shared"};return{id:e.connectorid,name:e.name,displayName:e.displayname||e.name,description:e.description,connectorType:i[e.connectortype||1]||"Custom",iconUri:e.iconuri,isManaged:e.ismanaged||!1,isCustomizable:((c=e.iscustomizable)==null?void 0:c.Value)??!0,capabilities:t,connectionParameters:s,owner:e.ownerid||"Unknown",modifiedOn:e.modifiedon||new Date().toISOString(),modifiedBy:e.modifiedby||"Unknown",policy:e.policytemplateinstances,apiDefinition:e.openapidefinition}}}export{y as CustomConnectorDiscovery};
|
|
2
|
+
//# sourceMappingURL=CustomConnectorDiscovery-TvNjElP3.js.map
|