Skip to content

Conversation

@IsmaelMartinez
Copy link
Owner

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:

{
  "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

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
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @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

  • Microsoft Graph API Client: Introduces a new GraphApiClient module to interact with the Microsoft Graph API, supporting endpoints for user profiles, calendar events, mail messages, and presence information, complete with token caching and error handling.
  • Token Acquisition via ReactHandler: Enhances the ReactHandler with an acquireToken() method, allowing the main process to securely obtain authentication tokens for Graph API and other resources by leveraging existing Teams authentication infrastructure.
  • Configurable Feature Flag: Adds a graphApi configuration option, disabled by default, enabling administrators to control the availability of Graph API integration, ensuring it's an opt-in feature.
  • IPC Communication: Implements seven new Inter-Process Communication (IPC) handlers to expose Graph API functionalities to the renderer process, ensuring secure and validated access to these new capabilities.
  • Seamless Browser Integration: Integrates the ReactHandler into the browser context via preload.js and exposes it globally as window.teamsForLinuxReactHandler, facilitating token acquisition from the renderer process.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a 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.

Comment on lines 165 to 185
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
};
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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
        };
      }

Comment on lines 210 to 225
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);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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
Comment on lines 153 to 201
// 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();
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

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.
@sonarqubecloud
Copy link

@IsmaelMartinez IsmaelMartinez moved this to In Progress in 2.x Nov 16, 2025
@IsmaelMartinez
Copy link
Owner Author

blocked by #1959

@IsmaelMartinez IsmaelMartinez added the blocked This issue depends on external projects label Nov 16, 2025
@IsmaelMartinez IsmaelMartinez marked this pull request as draft November 17, 2025 05:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

blocked This issue depends on external projects

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

3 participants