Skip to content

msteams: add member-info action#57528

Merged
BradGroux merged 1 commit intoopenclaw:mainfrom
sudie-codes:feat/msteams-member-info
Mar 31, 2026
Merged

msteams: add member-info action#57528
BradGroux merged 1 commit intoopenclaw:mainfrom
sudie-codes:feat/msteams-member-info

Conversation

@sudie-codes
Copy link
Copy Markdown
Contributor

Summary

  • Adds member-info message action to the MS Teams extension
  • Fetches user profile (displayName, mail, jobTitle, etc.) via Microsoft Graph /users/{id} endpoint
  • Follows existing action handler patterns in channel.ts

Test plan

  • Unit tests for getMemberInfoMSTeams (happy path, sparse data, error propagation)
  • pnpm test -- extensions/msteams passes
  • pnpm check passes

🤖 Generated with Claude Code

@openclaw-barnacle openclaw-barnacle bot added channel: msteams Channel integration: msteams size: S labels Mar 30, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 30, 2026

Greptile Summary

This PR adds a member-info message action to the MS Teams channel extension. When invoked, it calls the Microsoft Graph /users/{id} endpoint (with a $select projection for id, displayName, mail, jobTitle, userPrincipalName, and officeLocation) and returns the mapped profile fields.

The change is well-scoped and follows existing conventions in channel.ts precisely — userId is validated before the runtime call, encodeURIComponent prevents path-injection issues, and the result is surfaced via the standard jsonMSTeamsOkActionResult helper. The new graph-members.ts module is thin, focused, and integrates cleanly with the shared fetchGraphJson / resolveGraphToken infrastructure.

Findings:

  • GraphUserProfile in graph-members.ts redeclares four fields already present in GraphUser (from graph.ts); extending GraphUser would reduce duplication.
  • The sparse-data test mock omits $select-ed fields entirely (making them undefined), whereas the real Microsoft Graph API returns null for unset optional fields. This misalignment could mislead future callers who rely on strict === undefined checks, and the type declarations should arguably be string | null | undefined to match runtime reality.

Confidence Score: 5/5

This PR is safe to merge; all findings are minor style and test-accuracy suggestions with no functional impact on the happy path.

No P0 or P1 issues found. Both open findings are P2: one is a minor type-duplication cleanup and the other is a test-mock accuracy nuance that doesn't affect production behaviour. The implementation follows established patterns, input is encoded properly, and unit tests cover the key scenarios.

No files require special attention.

Important Files Changed

Filename Overview
extensions/msteams/src/graph-members.ts New module that fetches a Microsoft Graph user by ID with a $select projection; clean and correctly uses encodeURIComponent. Minor: GraphUserProfile duplicates four fields already declared in the shared GraphUser type.
extensions/msteams/src/graph-members.test.ts Unit tests cover happy path, sparse data, and error propagation correctly. Sparse-data mock returns absent fields as undefined rather than null, which may not reflect real Graph API responses for $select-requested fields.
extensions/msteams/src/channel.ts Adds member-info to the action list and implements the handler; validates userId, delegates to the new runtime function, and returns a JSON result. Follows the exact same pattern as neighbouring action handlers.
extensions/msteams/src/channel.runtime.ts Registers getMemberInfoMSTeams in the runtime export map alphabetically; straightforward wiring change with no issues.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/msteams/src/graph-members.ts
Line: 4-11

Comment:
**`GraphUserProfile` partially duplicates `GraphUser`**

The existing `GraphUser` type in `graph.ts` already declares `id`, `displayName`, `mail`, and `userPrincipalName` as optional strings. `GraphUserProfile` redeclares all four of those fields verbatim and then adds `jobTitle` and `officeLocation`.

Consider extending `GraphUser` to avoid the duplication:

```suggestion
type GraphUserProfile = import("./graph.js").GraphUser & {
  jobTitle?: string;
  officeLocation?: string;
};
```

Or, if you prefer to keep the import explicit, import `GraphUser` at the top of the file and write:
```ts
type GraphUserProfile = GraphUser & {
  jobTitle?: string;
  officeLocation?: string;
};
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: extensions/msteams/src/graph-members.test.ts
Line: 62-78

Comment:
**Test mock for sparse data may not reflect real Graph API behaviour**

The mock returns an object with only `id` and `displayName`, leaving the other selected fields absent (JavaScript `undefined`). However, the Microsoft Graph API — when asked for fields via `$select` — typically returns those fields with `null` rather than omitting them entirely. This means:

1. `GetMemberInfoMSTeamsResult` and `GraphUserProfile` typing would more accurately read `string | null | undefined` for those optional fields.
2. The assertion `expect(result).toEqual({ user: { ..., mail: undefined, ... } })` wouldn't hold against a real Graph response where `mail` is `null`.

The inconsistency doesn't cause a runtime error today (falsy checks work for both), but it means the type contract doesn't match what the Graph API actually produces, and a strict-equality check on `undefined` by a future consumer would silently fail. Adjusting the mock to use `null` for absent fields and widening the types to `string | null | undefined` would align with the codebase contract from `fetchGraphJson`.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "msteams: add member-info action via Grap..." | Re-trigger Greptile

Comment on lines +4 to +11
type GraphUserProfile = {
id?: string;
displayName?: string;
mail?: string;
jobTitle?: string;
userPrincipalName?: string;
officeLocation?: string;
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 GraphUserProfile partially duplicates GraphUser

The existing GraphUser type in graph.ts already declares id, displayName, mail, and userPrincipalName as optional strings. GraphUserProfile redeclares all four of those fields verbatim and then adds jobTitle and officeLocation.

Consider extending GraphUser to avoid the duplication:

Suggested change
type GraphUserProfile = {
id?: string;
displayName?: string;
mail?: string;
jobTitle?: string;
userPrincipalName?: string;
officeLocation?: string;
};
type GraphUserProfile = import("./graph.js").GraphUser & {
jobTitle?: string;
officeLocation?: string;
};

Or, if you prefer to keep the import explicit, import GraphUser at the top of the file and write:

type GraphUserProfile = GraphUser & {
  jobTitle?: string;
  officeLocation?: string;
};
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/msteams/src/graph-members.ts
Line: 4-11

Comment:
**`GraphUserProfile` partially duplicates `GraphUser`**

The existing `GraphUser` type in `graph.ts` already declares `id`, `displayName`, `mail`, and `userPrincipalName` as optional strings. `GraphUserProfile` redeclares all four of those fields verbatim and then adds `jobTitle` and `officeLocation`.

Consider extending `GraphUser` to avoid the duplication:

```suggestion
type GraphUserProfile = import("./graph.js").GraphUser & {
  jobTitle?: string;
  officeLocation?: string;
};
```

Or, if you prefer to keep the import explicit, import `GraphUser` at the top of the file and write:
```ts
type GraphUserProfile = GraphUser & {
  jobTitle?: string;
  officeLocation?: string;
};
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +62 to +78
});

const result = await getMemberInfoMSTeams({
cfg: {} as OpenClawConfig,
userId: "user-456",
});

expect(result).toEqual({
user: {
id: "user-456",
displayName: "Bob",
mail: undefined,
jobTitle: undefined,
userPrincipalName: undefined,
officeLocation: undefined,
},
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Test mock for sparse data may not reflect real Graph API behaviour

The mock returns an object with only id and displayName, leaving the other selected fields absent (JavaScript undefined). However, the Microsoft Graph API — when asked for fields via $select — typically returns those fields with null rather than omitting them entirely. This means:

  1. GetMemberInfoMSTeamsResult and GraphUserProfile typing would more accurately read string | null | undefined for those optional fields.
  2. The assertion expect(result).toEqual({ user: { ..., mail: undefined, ... } }) wouldn't hold against a real Graph response where mail is null.

The inconsistency doesn't cause a runtime error today (falsy checks work for both), but it means the type contract doesn't match what the Graph API actually produces, and a strict-equality check on undefined by a future consumer would silently fail. Adjusting the mock to use null for absent fields and widening the types to string | null | undefined would align with the codebase contract from fetchGraphJson.

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/msteams/src/graph-members.test.ts
Line: 62-78

Comment:
**Test mock for sparse data may not reflect real Graph API behaviour**

The mock returns an object with only `id` and `displayName`, leaving the other selected fields absent (JavaScript `undefined`). However, the Microsoft Graph API — when asked for fields via `$select` — typically returns those fields with `null` rather than omitting them entirely. This means:

1. `GetMemberInfoMSTeamsResult` and `GraphUserProfile` typing would more accurately read `string | null | undefined` for those optional fields.
2. The assertion `expect(result).toEqual({ user: { ..., mail: undefined, ... } })` wouldn't hold against a real Graph response where `mail` is `null`.

The inconsistency doesn't cause a runtime error today (falsy checks work for both), but it means the type contract doesn't match what the Graph API actually produces, and a strict-equality check on `undefined` by a future consumer would silently fail. Adjusting the mock to use `null` for absent fields and widening the types to `string | null | undefined` would align with the codebase contract from `fetchGraphJson`.

How can I resolve this? If you propose a fix, please make it concise.

@BradGroux BradGroux self-assigned this Mar 31, 2026
@BradGroux
Copy link
Copy Markdown
Contributor

Reviewed. Looks good to merge.

@BradGroux BradGroux merged commit 4e67e7c into openclaw:main Mar 31, 2026
34 of 38 checks passed
pgondhi987 pushed a commit to pgondhi987/openclaw that referenced this pull request Mar 31, 2026
pgondhi987 pushed a commit to pgondhi987/openclaw that referenced this pull request Mar 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: msteams Channel integration: msteams size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants