Refactor code structure for improved readability and maintainability
This commit is contained in:
302
README.md
302
README.md
@@ -1,204 +1,225 @@
|
||||
# Power Apps Code Apps — Part 2: Connectors, Dataverse & Outlook
|
||||
# Power Apps Code Apps — Part 2: Connectors & Outlook
|
||||
|
||||
> Build a full Outlook-like email client in a code app, wired to real Power Platform connectors.
|
||||
> Full email client in a code app, built with React + Fluent UI v9, powered by the Outlook connector.
|
||||
|
||||
This is the second article in the series. Make sure you have completed [Part 1](https://www.thatsagoodquestion.info/power-apps-code-apps) 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've completed Part 1 before continuing.
|
||||
|
||||
## What you will build
|
||||
---
|
||||
|
||||
A complete email client (Bandeja de entrada, Enviados, Borradores, Papelera) with:
|
||||
- Folder navigation
|
||||
- Email list with unread indicators
|
||||
- Email detail view with reply/forward
|
||||
- Compose modal
|
||||
- Real-time search
|
||||
## What you'll build
|
||||
|
||||
All powered by the Outlook connector — the same connector used by millions of Microsoft 365 users.
|
||||
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
|
||||
- **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
|
||||
|
||||
All built with **Fluent UI v9** (`@fluentui/react-components`) and the **Outlook connector** — the same connector available to 1,400+ Power Platform integrations.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Power Apps environment with a licensed user
|
||||
- `pac` CLI installed (`dotnet tool install -g Microsoft.PowerApps.CLI`)
|
||||
- Node.js 18+ and npm
|
||||
- A Microsoft 365 account (for the Outlook connector)
|
||||
- Connections already created in [make.powerapps.com](https://make.powerapps.com)
|
||||
| Requirement | Version |
|
||||
|---|---|
|
||||
| **Node.js** | 18+ |
|
||||
| **npm** | 9+ |
|
||||
| **PAC CLI** | 2.6+ (`dotnet tool install -g Microsoft.PowerApps.CLI`) |
|
||||
| **Power Apps environment** | Licensed user |
|
||||
| **Microsoft 365 account** | For the Outlook connector |
|
||||
|
||||
---
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
# 1. Clone & install
|
||||
git clone <this-repo>
|
||||
cd power-apps-codeapps-blog-part2
|
||||
npm install
|
||||
|
||||
# 2. Run locally
|
||||
npm run dev
|
||||
# → opens at http://localhost:5173
|
||||
|
||||
# 3. Build for production
|
||||
npm run build
|
||||
```
|
||||
|
||||
> The app uses mock data by default. To connect to real Outlook data, follow the connector setup below.
|
||||
|
||||
---
|
||||
|
||||
## Project structure
|
||||
|
||||
```
|
||||
power-apps-codeapps-blog-part2/
|
||||
├── index.html HTML shell
|
||||
├── package.json Dependencies & scripts
|
||||
├── tsconfig.json TypeScript config
|
||||
├── vite.config.ts Vite dev server & build
|
||||
├── power.config.json Power Platform app manifest (required by PAC CLI)
|
||||
└── src/
|
||||
├── main.tsx Entry point — FluentProvider + theme
|
||||
├── App.tsx Main UI: nav rail, folders, email list, reading pane
|
||||
├── index.css Minimal reset (Fluent handles all component styles)
|
||||
├── types/
|
||||
│ └── index.ts Shared TypeScript types (Email, Folder, UserProfile)
|
||||
└── services/
|
||||
└── OutlookService.ts Mock connector (mirrors real generated service API)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Your Code App │
|
||||
│ React UI → Service Layer → Connector Client │
|
||||
│ ↓ │
|
||||
│ ┌─────────────────────────────+ │
|
||||
│ │ Power Platform Connectors │ │
|
||||
│ │ ─────────────────────────── │ │
|
||||
│ │ Outlook │ Dataverse │ … │ │
|
||||
│ └─────────────────────────────+ │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
┌──────────────────────────────────────────────────────┐
|
||||
│ Your Code App │
|
||||
│ │
|
||||
│ React + Fluent UI → Service Layer → Connector │
|
||||
│ ↕ │
|
||||
│ ┌───────────────────────────────────┐ │
|
||||
│ │ Power Platform Connectors │ │
|
||||
│ │ ───────────────────────────────── │ │
|
||||
│ │ Outlook │ Dataverse │ Custom │ │
|
||||
│ └───────────────────────────────────┘ │
|
||||
└──────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Step 1 — Add a data source (connector)
|
||||
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`.
|
||||
|
||||
The `pac code add-data-source` command wires a connector to your app and auto-generates typed TypeScript model and service files.
|
||||
---
|
||||
|
||||
### Get your connection metadata
|
||||
## Connecting to real data
|
||||
|
||||
**Option A — PAC CLI (recommended)**
|
||||
### 1. Check your connections
|
||||
|
||||
```bash
|
||||
pac connection list
|
||||
```
|
||||
|
||||
You'll see a table:
|
||||
|
||||
| Connection Name | Connection ID | API Name |
|
||||
|---|---|---|
|
||||
| shared_office365 | `aaaaaaaa-0000-...` | `shared_office365` |
|
||||
| shared_outlook | `bbbbbbbb-1111-...` | `shared_outlook` |
|
||||
|
||||
**Option B — Power Apps URL**
|
||||
|
||||
1. Go to [make.powerapps.com](https://make.powerapps.com) → **Data** → **Connections**
|
||||
2. Click your connection
|
||||
3. The URL contains both the **API name** and **Connection ID**:
|
||||
Example output:
|
||||
|
||||
```
|
||||
https://make.powerapps.com/connections/aaaaaaa-0000-.../details
|
||||
└──────────────┘ └──────────────┘
|
||||
api name connection id
|
||||
Connected as lago@powerplatform.top
|
||||
Id Name API Id Status
|
||||
4839c34829284206bf6a11d4ce577491 Outlook.com /providers/Microsoft.PowerApps/apis/shared_outlook Connected
|
||||
```
|
||||
|
||||
### Add the data source
|
||||
### 2. Add the connector
|
||||
|
||||
```bash
|
||||
pac code add-data-source -a shared_outlook -c aaaaaaaa-0000-1111-bbbb-cccccccccccc
|
||||
pac code add-data-source \
|
||||
-a shared_outlook \
|
||||
-c 4839c34829284206bf6a11d4ce577491
|
||||
```
|
||||
|
||||
After running, you'll find two new files:
|
||||
This auto-generates typed TypeScript files:
|
||||
|
||||
```
|
||||
src/
|
||||
connectors/
|
||||
shared_outlook/
|
||||
OutlookModel.ts ← typed request/response types
|
||||
OutlookService.ts ← service with all connector actions
|
||||
src/connectors/shared_outlook/
|
||||
├── OutlookModel.ts ← request/response types
|
||||
└── OutlookService.ts ← typed service with all connector actions
|
||||
```
|
||||
|
||||
The generated service wraps every action from the connector as a typed TypeScript method. No need to manually craft HTTP calls or worry about auth headers.
|
||||
|
||||
### Dataverse connector
|
||||
### 3. (Optional) Add Dataverse
|
||||
|
||||
```bash
|
||||
pac code add-data-source -a shared_commondataservice -c <connection-id> -t accounts -d default
|
||||
pac code add-data-source \
|
||||
-a shared_commondataservice \
|
||||
-c <connection-id> \
|
||||
-t accounts \
|
||||
-d default
|
||||
```
|
||||
|
||||
This generates `DataverseModel.ts` and `DataverseService.ts` with full type safety for Dataverse tables.
|
||||
### 4. Swap mock for real service
|
||||
|
||||
## Step 2 — Use the connector in your app
|
||||
Replace the import in `App.tsx`:
|
||||
|
||||
Here's the theoretical model for how code apps interact with connectors:
|
||||
|
||||
```typescript
|
||||
// The Power Platform client library exposes a typed service.
|
||||
// The service is injected or instantiated with your connection reference.
|
||||
import { OutlookService } from '../connectors/shared_outlook';
|
||||
|
||||
const outlook = new OutlookService();
|
||||
|
||||
// All methods return typed Promises — no manual fetch needed
|
||||
const messages = await outlook.getMessages('inbox');
|
||||
const profile = await outlook.getProfile();
|
||||
|
||||
// Actions are also typed
|
||||
await outlook.sendMessage({
|
||||
to: 'colleague@company.com',
|
||||
subject: 'Sprint planning',
|
||||
body: 'Hi, let\'s sync tomorrow...',
|
||||
});
|
||||
```diff
|
||||
- import { OutlookService } from './services/OutlookService';
|
||||
+ import { OutlookService } from './connectors/shared_outlook/OutlookService';
|
||||
```
|
||||
|
||||
### Connection reference pattern
|
||||
The method signatures are identical — no other changes needed.
|
||||
|
||||
Rather than binding directly to a user-specific connection, code apps use a **connection reference** — a solution component that points to a connection. This allows:
|
||||
---
|
||||
|
||||
- **Environment promotion**: connections change per environment (dev/staging/prod) without code changes
|
||||
- **Managed identity support**: use system-assigned identities instead of user credentials
|
||||
- **Centralised governance**: IT can audit which apps use which connectors
|
||||
## Connection references (theory)
|
||||
|
||||
## Step 3 — Explore the example
|
||||
Code apps use **connection references** instead of binding directly to a user-specific connection. Benefits:
|
||||
|
||||
The `src/` directory in this repo contains a complete, working example:
|
||||
| Benefit | Description |
|
||||
|---|---|
|
||||
| **Environment promotion** | Connections change per environment (dev → staging → prod) 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 |
|
||||
|
||||
```
|
||||
src/
|
||||
App.tsx ← main UI (folders, email list, detail, compose)
|
||||
index.css ← dark theme styles
|
||||
main.tsx ← React entry point
|
||||
types/
|
||||
index.ts ← shared TypeScript types
|
||||
services/
|
||||
OutlookService.ts ← mock connector (for demo without real creds)
|
||||
```
|
||||
---
|
||||
|
||||
The mock `OutlookService` mirrors the interface of a real generated connector. Swap it for the real generated service and the app works against your actual Outlook data.
|
||||
## Fluent UI v9
|
||||
|
||||
## Step 4 — Run locally
|
||||
This project uses [Fluent UI v9](https://react.fluentui.dev/) — the same design system that powers Microsoft 365:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
- `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`
|
||||
|
||||
Opens at `http://localhost:5173`. The app hot-reloads as you edit.
|
||||
---
|
||||
|
||||
## Step 5 — Deploy to Power Platform
|
||||
## NPM scripts
|
||||
|
||||
| Script | Description |
|
||||
|---|---|
|
||||
| `npm run dev` | Start Vite dev server at `http://localhost:5173` |
|
||||
| `npm run build` | Type-check + production build |
|
||||
| `npm run preview` | Preview the production build locally |
|
||||
|
||||
---
|
||||
|
||||
## Deploy to Power Platform
|
||||
|
||||
```bash
|
||||
pac code deploy
|
||||
```
|
||||
|
||||
This packages the app and registers it in your Power Apps environment. After deploying, add the code app to a solution and publish.
|
||||
This packages the app and registers it in your environment. Then add it to a solution and publish.
|
||||
|
||||
## How connectors work in code apps (theory)
|
||||
---
|
||||
|
||||
Code apps access connectors through the **Power Platform client library**. The client library:
|
||||
|
||||
1. **Discovers available connectors** from the runtime environment
|
||||
2. **Manages authentication** via the user's Microsoft 365 session (single sign-on)
|
||||
3. **Exposes typed service interfaces** generated at add-time
|
||||
4. **Handles pagination, throttling, and error responses** transparently
|
||||
|
||||
This means you call `outlookService.getMessages()` just like any other async function — no base URLs, no API keys, no manual token refresh.
|
||||
|
||||
### Supported connectors
|
||||
|
||||
Code apps support **1,400+ connectors** including:
|
||||
|
||||
| Category | Connectors |
|
||||
|---|---|
|
||||
| Microsoft 365 | Outlook, Teams, SharePoint, OneDrive, Planner |
|
||||
| Dataverse | Common Data Service (Dataverse) |
|
||||
| Data | SQL Server, Dataverse, OData |
|
||||
| SaaS | Salesforce, SAP, ServiceNow, Slack |
|
||||
| Custom | Any standard REST connector |
|
||||
|
||||
### Unsupported connectors
|
||||
|
||||
As of the initial release:
|
||||
|
||||
- Excel Online (Business)
|
||||
- Excel Online (OneDrive)
|
||||
|
||||
## Key differences from canvas apps
|
||||
## Key differences from Canvas Apps
|
||||
|
||||
| Aspect | Canvas Apps | Code Apps |
|
||||
|---|---|---|
|
||||
| UI Framework | Power Fx formula bar | React/Vue/Svelte (your choice) |
|
||||
| Connector Access | Built-in connector picker | PAC CLI + typed client library |
|
||||
| Data Model | Implicit, delegation-based | Explicit TypeScript types |
|
||||
| Deployment | Packaged by Power Platform | `pac code deploy` + solution |
|
||||
| CI/CD | Limited | Full git-native workflow |
|
||||
| **UI Framework** | Power Fx formula bar | React / Vue / Svelte |
|
||||
| **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 |
|
||||
| **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
|
||||
|
||||
@@ -209,4 +230,5 @@ As of the initial release:
|
||||
- [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
|
||||
- [Connector classification (DLP)](https://learn.microsoft.com/en-us/power-platform/admin/dlp-connector-classification) — Microsoft Learn
|
||||
- [Part 1 — Power Apps code apps: tutorial, best practices, and production patterns](https://www.thatsagoodquestion.info/power-apps-code-apps)
|
||||
- [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
|
||||
|
||||
Reference in New Issue
Block a user