Private
Public Access
1
0

feat: integrate Microsoft Power Apps SDK and enhance email handling

- Added dependency for @microsoft/power-apps to package.json.
- Updated power.config.json to include appId, environmentId, and connection references.
- Implemented sanitisation function for HTML content in App.tsx to prevent XSS.
- Enhanced error handling in email loading and marking as read functionalities.
- Updated email display logic to handle HTML content and previews.
- Refactored OutlookService to use auto-generated service from @microsoft/power-apps.
- Added new methods for sending, marking as read, and deleting emails in OutlookService.
- Updated types for Email to include bodyPreview, isHtml, hasAttachments, and importance.
- Configured Vite to exclude @microsoft/power-apps/data from the build.
- Created .gitignore to exclude build artifacts and environment files.
- Added local development shim for @microsoft/power-apps/data to support local testing.
- Defined type stubs for @microsoft/power-apps/data to facilitate local development.
This commit is contained in:
Lago
2026-04-16 22:42:40 +02:00
parent 96f76e21a7
commit 0a1d96a40f
35 changed files with 2558 additions and 57689 deletions

238
README.md
View File

@@ -2,22 +2,22 @@
> Full email client in a code app, built with React + Fluent UI v9, powered by the Outlook connector.
This is the companion repo for [Part 2 of the blog series](https://www.thatsagoodquestion.info/power-apps-code-apps). Make sure you've completed Part 1 before continuing.
This is the companion repo for [Part 2 of the blog series](https://www.thatsagoodquestion.info/power-apps-code-apps). Make sure you have completed Part 1 before continuing.
---
## What you'll build
## What you will build
An Outlook-like email client with:
- **Folder navigation** — Inbox, Sent, Drafts, Trash
- **Email list** — unread indicators, avatar colors, relative timestamps
- **Reading pane** — full email body with Reply / Forward / Delete
- **Reading pane** — full email body (HTML-safe) with Reply / Forward / Delete
- **Compose dialog** — Fluent UI v9 dialog with validation
- **Connections panel** — visual reference of connectors + PAC CLI commands
- **Real-time search** — filter messages instantly by sender or subject
- **Real-time search** — filter by sender or subject
All built with **Fluent UI v9** (`@fluentui/react-components`) and the **Outlook connector** — the same connector available to 1,400+ Power Platform integrations.
All built with **Fluent UI v9** (`@fluentui/react-components`) and the **Outlook connector**.
---
@@ -28,7 +28,7 @@ All built with **Fluent UI v9** (`@fluentui/react-components`) and the **Outlook
| **Node.js** | 18+ |
| **npm** | 9+ |
| **PAC CLI** | 2.6+ (`dotnet tool install -g Microsoft.PowerApps.CLI`) |
| **Power Apps environment** | Licensed user |
| **Power Apps environment** | Licensed user with code apps enabled |
| **Microsoft 365 account** | For the Outlook connector |
---
@@ -41,16 +41,18 @@ git clone <this-repo>
cd power-apps-codeapps-blog-part2
npm install
# 2. Run locally
# 2. Run locally (UI only, no connector data)
npm run dev
# → opens at http://localhost:5173
# 3. Build for production
npm run build
# 3. Run locally WITH real connector data
# Terminal 1:
npm run dev
# Terminal 2:
pac code run -a http://localhost:5173
# 4. Open the URL printed by pac code run in your browser
```
> The app uses mock data by default. To connect to real Outlook data, follow the connector setup below.
---
## Project structure
@@ -60,16 +62,26 @@ power-apps-codeapps-blog-part2/
├── index.html HTML shell
├── package.json Dependencies & scripts
├── tsconfig.json TypeScript config
├── vite.config.ts Vite dev server & build
├── vite.config.ts Vite dev server & build config
├── power.config.json Power Platform app manifest (required by PAC CLI)
├── .gitignore Excludes dist/, .power/, node_modules/
└── src/
├── main.tsx Entry point — FluentProvider + theme
├── main.tsx Entry point — FluentProvider + light/dark theme
├── App.tsx Main UI: nav rail, folders, email list, reading pane
├── index.css Minimal reset (Fluent handles all component styles)
├── index.css Minimal reset (Fluent UI handles component styles)
├── shims/
│ └── power-apps-data.ts Local dev shim for @microsoft/power-apps/data
├── types/
── index.ts Shared TypeScript types (Email, Folder, UserProfile)
└── services/
└── OutlookService.ts Mock connector (mirrors real generated service API)
── index.ts Shared types (Email, Folder, FOLDER_PATH_MAP)
│ └── power-apps.d.ts Type declarations for the Power Platform runtime
├── services/
│ └── OutlookService.ts Adapter wrapping the generated Outlook_comService
└── generated/ Auto-generated by pac code add-data-source
├── index.ts
├── models/
│ └── Outlook_comModel.ts Typed request/response interfaces
└── services/
└── Outlook_comService.ts All connector operations as static methods
```
---
@@ -80,21 +92,23 @@ power-apps-codeapps-blog-part2/
┌──────────────────────────────────────────────────────┐
│ Your Code App │
│ │
│ React + Fluent UI → Service Layer → Connector
│ React + Fluent UI → OutlookService → Generated
│ (adapter) Service │
│ ↕ │
│ ┌───────────────────────────────────┐ │
│ │ Power Platform Connectors │ │
│ │ ───────────────────────────────── │ │
│ │ Outlook │ Dataverse Custom │ │
│ │ Power Platform Runtime │ │
│ │ ───────────────────────────── │ │
│ │ @microsoft/power-apps/data │ │
│ │ → Outlook connector API │ │
│ └───────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
```
The app talks to connectors through a **typed service layer**. In development, `OutlookService.ts` returns mock data. In production, swap it for the auto-generated service from `pac code add-data-source`.
`src/services/OutlookService.ts` wraps the auto-generated `Outlook_comService` and maps its responses to the UI`s `Email` type. All real connector calls go through the Power Platform runtime SDK.
---
## Connecting to real data
## Setting up your own connections
### 1. Check your connections
@@ -105,70 +119,144 @@ pac connection list
Example output:
```
Connected as lago@powerplatform.top
Connected as user@contoso.com
Id Name API Id Status
4839c34829284206bf6a11d4ce577491 Outlook.com /providers/Microsoft.PowerApps/apis/shared_outlook Connected
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Outlook.com /providers/Microsoft.PowerApps/apis/shared_outlook Connected
```
### 2. Add the connector
### 2. Add the connector to your code app
```bash
pac code add-data-source \
-a shared_outlook \
-c 4839c34829284206bf6a11d4ce577491
-c <your-connection-id>
```
This auto-generates typed TypeScript files:
This auto-generates typed files in `src/generated/`:
```
src/connectors/shared_outlook/
├── OutlookModel.ts ← request/response types
└── OutlookService.ts ← typed service with all connector actions
src/generated/
├── models/Outlook_comModel.ts ← request/response types
└── services/Outlook_comService.ts ← typed service with all operations
```
### 3. (Optional) Add Dataverse
### 3. Update `power.config.json`
Replace the placeholder values with your real IDs:
```json
{
"environmentId": "<your-environment-id>",
"connectionReferences": {
"<your-connection-reference-id>": {
"id": "/providers/Microsoft.PowerApps/apis/shared_outlook",
...
}
}
}
```
### 4. Run with real connections
```bash
pac code add-data-source \
-a shared_commondataservice \
-c <connection-id> \
-t accounts \
-d default
# Terminal 1 — Vite dev server
npm run dev
# Terminal 2 — Power Platform connection proxy
pac code run -a http://localhost:5173
```
### 4. Swap mock for real service
Replace the import in `App.tsx`:
```diff
- import { OutlookService } from './services/OutlookService';
+ import { OutlookService } from './connectors/shared_outlook/OutlookService';
```
The method signatures are identical — no other changes needed.
Open the URL printed by `pac code run` in your browser.
---
## Connection references (theory)
## Removing an unused data source / connection
Code apps use **connection references** instead of binding directly to a user-specific connection. Benefits:
If you added a connection by mistake, or the schema changed and you need to refresh:
### Delete the data source
```bash
pac code delete-data-source -a <apiName> -ds <dataSourceName>
```
For example, to remove an Outlook data source named `outlook`:
```bash
pac code delete-data-source -a shared_outlook -ds outlook
```
This removes:
- The entry from `power.config.json` → `connectionReferences`
- The generated files under `src/generated/`
### Clean up manually (if needed)
If the CLI does not fully clean up:
1. **`power.config.json`** — Remove the connection reference entry from `connectionReferences`
2. **`src/generated/`** — Delete the corresponding `*Model.ts` and `*Service.ts` files
3. **`.power/schemas/`** — Delete the connector schema folder (e.g., `.power/schemas/outlook/`)
4. **`src/services/OutlookService.ts`** — Remove or update the adapter that imports from the deleted generated service
### Verify
```bash
npx tsc --noEmit # should compile without errors
npm run build # should produce a clean build
```
> **Important**: There is no `refresh` command yet. If a connector schema changes, delete the data source and re-add it.
---
## Available connector methods
After running `pac code add-data-source`, the generated `Outlook_comService.ts` exposes these static methods:
| Method | Description |
|---|---|
| `GetEmails(folderPath, ...)` | Get emails from a folder |
| `GetEmailsV2(folderPath, ...)` | V2 with more filter options |
| `GetEmail(messageId)` | Get a single email by ID |
| `SendEmail(message)` | Send email (plain text) |
| `SendEmailV2(message)` | Send email (HTML) |
| `MarkAsRead(messageId)` | Mark as read |
| `DeleteEmail(messageId)` | Delete email |
| `Move(messageId, folderPath)` | Move to another folder |
| `Flag(messageId)` | Flag an email |
| `ReplyTo / ReplyToV2 / ReplyToV3` | Reply (text/HTML) |
| `ForwardEmail(messageId, body)` | Forward |
All methods return `Promise<IOperationResult<T>>` — access the data via `result.data`.
---
## Connection references
Code apps use **connection references** instead of binding to user-specific connections:
| Benefit | Description |
|---|---|
| **Environment promotion** | Connections change per environment (devstagingprod) without code changes |
| **Managed identity** | Use system-assigned identities instead of user credentials |
| **Governance** | IT can audit which apps use which connectors via DLP policies |
| **Environment promotion** | Connections change per env (dev/staging/prod) without code changes |
| **Managed identity** | System-assigned identities instead of user credentials |
| **Governance** | IT audits via DLP policies |
---
## Fluent UI v9
## Deploy to Power Platform
This project uses [Fluent UI v9](https://react.fluentui.dev/) — the same design system that powers Microsoft 365:
```bash
# Build and push to your environment
npm run build
pac code push
```
- `FluentProvider` with `webLightTheme` / `webDarkTheme` (auto-detected)
- `makeStyles` for zero-runtime CSS-in-JS
- Design tokens (`tokens.colorBrandBackground`, etc.) for consistent theming
- All components: `Avatar`, `Badge`, `Button`, `Dialog`, `Input`, `Textarea`, `Spinner`, `Tooltip`, `Divider`, `ToolbarButton`
To target a specific solution:
```bash
pac code push --solution <solution-id>
```
---
@@ -182,16 +270,6 @@ This project uses [Fluent UI v9](https://react.fluentui.dev/) — the same desig
---
## Deploy to Power Platform
```bash
pac code deploy
```
This packages the app and registers it in your environment. Then add it to a solution and publish.
---
## Key differences from Canvas Apps
| Aspect | Canvas Apps | Code Apps |
@@ -200,35 +278,19 @@ This packages the app and registers it in your environment. Then add it to a sol
| **Connector access** | Built-in connector picker | PAC CLI + typed client library |
| **Data model** | Implicit, delegation-based | Explicit TypeScript types |
| **Styling** | Themed controls | Fluent UI / any CSS framework |
| **Deployment** | Packaged by Power Platform | `pac code deploy` + solution |
| **Deployment** | Packaged by Power Platform | `pac code push` |
| **Source control** | Limited | Full git-native workflow |
---
## Supported connectors
Code apps support **1,400+ connectors**:
| Category | Examples |
|---|---|
| **Microsoft 365** | Outlook, Teams, SharePoint, OneDrive, Planner |
| **Dataverse** | Common Data Service (Dataverse) |
| **Data** | SQL Server, OData |
| **SaaS** | Salesforce, SAP, ServiceNow, Slack |
| **Custom** | Any standard REST connector |
**Not yet supported:** Excel Online (Business), Excel Online (OneDrive).
---
## References
- [How to: Connect your code app to data](https://learn.microsoft.com/en-us/power-apps/developer/code-apps/how-to/connect-to-data) — Microsoft Learn
- [Power Apps code apps overview](https://learn.microsoft.com/en-us/power-apps/developer/code-apps/overview) — Microsoft Learn
- [Code apps architecture](https://learn.microsoft.com/en-us/power-apps/developer/code-apps/architecture) — Microsoft Learn
- [Use CLI to discover, create, and wire connectors](https://learn.microsoft.com/en-us/power-platform/release-plan/2026wave1/power-apps/use-cli-discover-create-wire-connectors-code-apps) — Microsoft Learn
- [pac connection list](https://learn.microsoft.com/en-us/power-platform/developer/cli/reference/connection#pac-connection-list) — PAC CLI reference
- [pac code add-data-source](https://learn.microsoft.com/en-us/power-platform/developer/cli/reference/code#pac-code-add-data-source) — PAC CLI reference
- [pac code delete-data-source](https://learn.microsoft.com/en-us/power-platform/developer/cli/reference/code#pac-code-delete-data-source) — PAC CLI reference
- [pac connection list](https://learn.microsoft.com/en-us/power-platform/developer/cli/reference/connection#pac-connection-list) — PAC CLI reference
- [Connector classification (DLP)](https://learn.microsoft.com/en-us/power-platform/admin/dlp-connector-classification) — Microsoft Learn
- [Fluent UI v9 — React components](https://react.fluentui.dev/) — Microsoft
- [Part 1: Power Apps code apps tutorial](https://www.thatsagoodquestion.info/power-apps-code-apps) — That's a good question