-
-
Notifications
You must be signed in to change notification settings - Fork 301
[Research]: Graph API integration #1958
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[Research]: Graph API integration #1958
Conversation
Implements Phase 1 of Microsoft Graph API integration for Teams for Linux,
enabling access to Microsoft Graph API endpoints using existing Teams
authentication infrastructure.
## Key Features
### 1. Graph API Client Module (app/graphApi/)
- New GraphApiClient class for Microsoft Graph API integration
- Token acquisition via existing Teams authentication provider
- Support for multiple Graph API endpoints:
- User profile (/me)
- Calendar events and calendar view
- Mail messages
- Presence information
- Calendar event creation/updates
- Automatic token caching with 5-minute expiry buffer
- Comprehensive error handling and logging
### 2. ReactHandler Token Acquisition
- Added `acquireToken()` method to ReactHandler
- Supports arbitrary resource URLs (Graph API, SharePoint, etc.)
- Leverages existing Teams authentication service
- Configurable token acquisition options (forceRenew, forceRefresh, etc.)
- Properly exposed to browser context via window.teamsForLinuxReactHandler
### 3. Configuration Support
- Added `graphApi` configuration option to config system
- Disabled by default (opt-in feature)
- Simple enable/disable flag in config.json
### 4. IPC Integration
- 7 new IPC handlers for Graph API operations:
- graph-api-get-user-profile
- graph-api-get-calendar-events
- graph-api-get-calendar-view
- graph-api-create-calendar-event
- graph-api-get-mail-messages
- graph-api-get-presence
- graph-api-get-diagnostics
- All channels added to IPC security allowlist
- Proper validation and error handling
### 5. Browser Integration
- ReactHandler properly initialized in preload.js
- Window exposure for JavaScript access
- Seamless integration with existing module loading pattern
## Implementation Details
- Uses existing Teams authentication tokens (no separate auth required)
- Graceful fallback when Graph API is disabled
- Follows existing project patterns (module structure, error handling, logging)
- Zero impact when feature is disabled
- No external dependencies added
## Usage
Enable in config.json:
```json
{
"graphApi": {
"enabled": true
}
}
```
Then access via IPC from renderer or use GraphApiClient from main process.
## Technical Architecture
```
Teams Auth Provider (Browser)
↓ (acquireToken via ReactHandler)
Graph API Client (Main Process)
↓ (HTTP requests with Bearer token)
Microsoft Graph API (graph.microsoft.com)
```
## Testing
- All code passes ESLint validation
- Follows existing Teams for Linux patterns
- Ready for integration testing
## Future Enhancements
This implements Phase 1 foundation. Future phases can add:
- Enhanced features (Phase 2): Email notifications, meeting scheduling
- Authentication improvements (Phase 3): Token refresh mechanisms
- UI components for Graph API features
- Additional Graph API endpoints as needed
Addresses issue #1832
Summary of ChangesHello @IsmaelMartinez, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces the foundational Phase 1 integration of the Microsoft Graph API into Teams for Linux. The primary goal is to enable access to various Microsoft Graph API endpoints, such as user profiles, calendar, mail, and presence, by leveraging the application's existing authentication infrastructure. This provides a secure and configurable mechanism for future enhancements that will utilize these new capabilities, while ensuring minimal impact when the feature is disabled. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces a significant new feature by integrating the Microsoft Graph API. The implementation is well-structured, with a clear separation of concerns between the main process API client (GraphApiClient) and the renderer-side token acquisition logic (ReactHandler). The changes are gated behind a configuration flag, which is a good practice for new features.
My review focuses on improving the robustness of the API client, reducing code duplication, and enhancing maintainability. I've suggested changes to handle API responses more gracefully and to refactor repetitive code blocks in both the API client and the IPC handler registration.
| const response = await fetch(url, requestOptions); | ||
| const data = await response.json(); | ||
|
|
||
| if (response.ok) { | ||
| logger.debug('[GRAPH_API] Request successful', { | ||
| status: response.status, | ||
| endpoint: url | ||
| }); | ||
| return { success: true, data }; | ||
| } else { | ||
| logger.warn('[GRAPH_API] Request failed', { | ||
| status: response.status, | ||
| error: data.error | ||
| }); | ||
| return { | ||
| success: false, | ||
| error: data.error?.message || 'API request failed', | ||
| status: response.status, | ||
| data | ||
| }; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current implementation of makeRequest can be fragile. It assumes all responses, including errors, will have a JSON body, which is not always true. For example, a successful DELETE request might return a 204 No Content status with an empty body, causing response.json() to fail. Similarly, some errors might return non-JSON responses. A more robust approach would be to read the response as text first, then parse it as JSON only if it's not empty.
const response = await fetch(url, requestOptions);
const responseText = await response.text();
// Safely parse JSON, handles empty bodies (e.g., 204 No Content)
const data = responseText ? JSON.parse(responseText) : null;
if (response.ok) {
logger.debug('[GRAPH_API] Request successful', {
status: response.status,
endpoint: url
});
return { success: true, data };
} else {
logger.warn('[GRAPH_API] Request failed', {
status: response.status,
error: data?.error
});
return {
success: false,
error: data?.error?.message || responseText || 'API request failed',
status: response.status,
data
};
}| async getCalendarEvents(options = {}) { | ||
| logger.debug('[GRAPH_API] Getting calendar events', options); | ||
|
|
||
| const params = new URLSearchParams(); | ||
| if (options.startDateTime) params.append('startDateTime', options.startDateTime); | ||
| if (options.endDateTime) params.append('endDateTime', options.endDateTime); | ||
| if (options.top) params.append('$top', options.top); | ||
| if (options.select) params.append('$select', options.select); | ||
| if (options.filter) params.append('$filter', options.filter); | ||
| if (options.orderby) params.append('$orderby', options.orderby); | ||
|
|
||
| const queryString = params.toString(); | ||
| const endpoint = queryString ? `/me/calendar/events?${queryString}` : '/me/calendar/events'; | ||
|
|
||
| return await this.makeRequest(endpoint); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic for building the OData query string is duplicated in getCalendarEvents and getMailMessages. This could be extracted into a private helper method to improve maintainability and reduce code duplication. This would also make it easier to add support for more OData query parameters in the future.
For example, you could create a helper like this:
_buildODataQuery(options) {
const params = new URLSearchParams();
const supportedParams = {
startDateTime: 'startDateTime',
endDateTime: 'endDateTime',
top: '$top',
select: '$select',
filter: '$filter',
orderby: '$orderby'
};
for (const [key, value] of Object.entries(options)) {
if (supportedParams[key] && value) {
params.append(supportedParams[key], value);
}
}
return params.toString();
}Then you could simplify getCalendarEvents and other methods.
app/index.js
Outdated
| // Graph API IPC handlers | ||
| ipcMain.handle("graph-api-get-user-profile", async () => { | ||
| if (!graphApiClient) { | ||
| return { success: false, error: "Graph API not enabled" }; | ||
| } | ||
| return await graphApiClient.getUserProfile(); | ||
| }); | ||
|
|
||
| ipcMain.handle("graph-api-get-calendar-events", async (_event, options) => { | ||
| if (!graphApiClient) { | ||
| return { success: false, error: "Graph API not enabled" }; | ||
| } | ||
| return await graphApiClient.getCalendarEvents(options); | ||
| }); | ||
|
|
||
| ipcMain.handle("graph-api-get-calendar-view", async (_event, startDateTime, endDateTime, options) => { | ||
| if (!graphApiClient) { | ||
| return { success: false, error: "Graph API not enabled" }; | ||
| } | ||
| return await graphApiClient.getCalendarView(startDateTime, endDateTime, options); | ||
| }); | ||
|
|
||
| ipcMain.handle("graph-api-create-calendar-event", async (_event, event) => { | ||
| if (!graphApiClient) { | ||
| return { success: false, error: "Graph API not enabled" }; | ||
| } | ||
| return await graphApiClient.createCalendarEvent(event); | ||
| }); | ||
|
|
||
| ipcMain.handle("graph-api-get-mail-messages", async (_event, options) => { | ||
| if (!graphApiClient) { | ||
| return { success: false, error: "Graph API not enabled" }; | ||
| } | ||
| return await graphApiClient.getMailMessages(options); | ||
| }); | ||
|
|
||
| ipcMain.handle("graph-api-get-presence", async () => { | ||
| if (!graphApiClient) { | ||
| return { success: false, error: "Graph API not enabled" }; | ||
| } | ||
| return await graphApiClient.getPresence(); | ||
| }); | ||
|
|
||
| ipcMain.handle("graph-api-get-diagnostics", async () => { | ||
| if (!graphApiClient) { | ||
| return { success: false, error: "Graph API not enabled" }; | ||
| } | ||
| return graphApiClient.getDiagnostics(); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The IPC handlers for the Graph API share a lot of boilerplate code, specifically the check for graphApiClient. This can be refactored to reduce duplication and make it easier to add new API handlers in the future. You could use a loop to register the handlers from a configuration object.
// Graph API IPC handlers
const graphApiHandlers = {
"graph-api-get-user-profile": (client) => client.getUserProfile(),
"graph-api-get-calendar-events": (client, options) => client.getCalendarEvents(options),
"graph-api-get-calendar-view": (client, startDateTime, endDateTime, options) => client.getCalendarView(startDateTime, endDateTime, options),
"graph-api-create-calendar-event": (client, event) => client.createCalendarEvent(event),
"graph-api-get-mail-messages": (client, options) => client.getMailMessages(options),
"graph-api-get-presence": (client) => client.getPresence(),
"graph-api-get-diagnostics": (client) => client.getDiagnostics(),
};
for (const [channel, handler] of Object.entries(graphApiHandlers)) {
ipcMain.handle(channel, async (_event, ...args) => {
if (!graphApiClient) {
return { success: false, error: "Graph API not enabled" };
}
return handler(graphApiClient, ...args);
});
}Based on code review feedback, this commit makes several improvements to the Graph API integration: ## 1. Robust JSON Response Handling The makeRequest method now properly handles: - Empty responses (204 No Content) - Non-JSON responses - Content-Type validation before parsing - Graceful error messages when JSON parsing fails This prevents crashes when Graph API returns empty or non-JSON responses (e.g., successful DELETE operations). ## 2. DRY Query String Building - Added _buildODataQuery() helper method - Supports common OData parameters: $top, $select, $filter, $orderby, $skip, $count, $search, $expand - Eliminates duplicated code in getCalendarEvents and getMailMessages - Makes it easier to add more OData endpoints in the future ## 3. Reduced IPC Handler Boilerplate Refactored Graph API IPC handlers using loop-based registration: - Reduced ~40 lines of duplicated code to ~15 lines - Easier to add new Graph API endpoints - Consistent error handling across all handlers - Cleaner, more maintainable code ## 4. Cross-Environment Window Access Changed window to globalThis.window in reactHandler.js for better compatibility across different JavaScript environments. ## Benefits - More robust error handling (no crashes on edge cases) - Better code maintainability (less duplication) - Easier extensibility (adding new endpoints is simpler) - Follows JavaScript best practices All changes pass ESLint validation.
|
|
blocked by #1959 |



Implements Phase 1 of Microsoft Graph API integration for Teams for Linux, enabling access to Microsoft Graph API endpoints using existing Teams authentication infrastructure.
Key Features
1. Graph API Client Module (app/graphApi/)
2. ReactHandler Token Acquisition
acquireToken()method to ReactHandler3. Configuration Support
graphApiconfiguration option to config system4. IPC Integration
5. Browser Integration
Implementation Details
Usage
Enable in config.json:
{ "graphApi": { "enabled": true } }Then access via IPC from renderer or use GraphApiClient from main process.
Technical Architecture
Testing
Future Enhancements
This implements Phase 1 foundation. Future phases can add:
Addresses issue #1832