Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions gemini-extension.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
{
"name": "google-workspace",
"version": "DEV",
"version": "0.0.2",
"contextFileName": "workspace-server${/}WORKSPACE-Context.md",
"mcpServers": {
"google-workspace": {
"program": "./workspace-server/dist/index.js",
"command": "node",
"args": ["scripts${/}start.js"],
"args": [
"scripts${/}start.js"
],
"cwd": "${extensionPath}"
}
}
}
}
27 changes: 27 additions & 0 deletions workspace-server/src/__tests__/services/ChatService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,33 @@ describe('ChatService', () => {
const response = JSON.parse(result.content[0].text);
expect(response.messages).toEqual(mockMessages);
});

it('should pass orderBy to the API', async () => {
const mockMessages = [
{ name: 'spaces/space1/messages/msg1', text: 'Hello' },
];

mockChatAPI.spaces.messages.list.mockResolvedValue({
data: {
messages: mockMessages,
},
});

const result = await chatService.getMessages({
spaceName: 'spaces/space1',
orderBy: 'createTime desc',
});

expect(mockChatAPI.spaces.messages.list).toHaveBeenCalledWith({
parent: 'spaces/space1',
pageSize: undefined,
pageToken: undefined,
orderBy: 'createTime desc',
});

const response = JSON.parse(result.content[0].text);
expect(response.messages).toEqual(mockMessages);
});
});

describe('sendDm', () => {
Expand Down
1 change: 1 addition & 0 deletions workspace-server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,7 @@ async function main() {
unreadOnly: z.boolean().optional().describe('Whether to return only unread messages.'),
pageSize: z.number().optional().describe('The maximum number of messages to return.'),
pageToken: z.string().optional().describe('The token for the next page of results.'),
orderBy: z.string().optional().describe('The order to list messages in (e.g., "createTime desc").'),
}
},
chatService.getMessages
Expand Down
63 changes: 26 additions & 37 deletions workspace-server/src/services/ChatService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,12 @@ export class ChatService {
}
}

public getMessages = async ({ spaceName, unreadOnly, pageSize, pageToken }: { spaceName: string, unreadOnly?: boolean, pageSize?: number, pageToken?: string }) => {
public getMessages = async ({ spaceName, unreadOnly, pageSize, pageToken, orderBy }: { spaceName: string, unreadOnly?: boolean, pageSize?: number, pageToken?: string, orderBy?: string }) => {
logToFile(`Listing messages for space: ${spaceName}`);
try {
const chat = await this.getChatClient();
let filter: string | undefined;

if (unreadOnly) {
const people = await this.getPeopleClient();
const person = await people.people.get({
Expand All @@ -203,47 +205,33 @@ export class ChatService {

const lastReadTime = currentUserMember?.lastReadTime;

if (!lastReadTime) {
if (lastReadTime) {
filter = `createTime > "${lastReadTime}"`;
} else {
logToFile(`No last read time found for user in space: ${spaceName}`);
// This can happen if the user has never read messages in the space.
// In this case, all messages are unread.
const res = await chat.spaces.messages.list({ parent: spaceName, pageSize, pageToken });
const messages = res.data.messages || [];
logToFile(`Successfully listed ${messages.length} unread messages for space: ${spaceName}`);
return { content: [{ type: "text" as const, text: JSON.stringify({ messages, nextPageToken: res.data.nextPageToken }) }] };
}
}

const res = await chat.spaces.messages.list({
parent: spaceName,
filter: `createTime > "${lastReadTime}"`,
pageSize,
pageToken,
});
const res = await chat.spaces.messages.list({
parent: spaceName,
filter,
pageSize,
pageToken,
orderBy,
});

const messages = res.data.messages || [];
logToFile(`Successfully listed ${messages.length} unread messages for space: ${spaceName}`);
return {
content: [{
type: "text" as const,
text: JSON.stringify({ messages, nextPageToken: res.data.nextPageToken })
}]
};
const messages = res.data.messages || [];
const logMessage = unreadOnly
? `Successfully listed ${messages.length} unread messages for space: ${spaceName}`
: `Successfully listed ${messages.length} messages for space: ${spaceName}`;
logToFile(logMessage);

} else {
const res = await chat.spaces.messages.list({
parent: spaceName,
pageSize,
pageToken,
});
const messages = res.data.messages || [];
logToFile(`Successfully listed ${messages.length} messages for space: ${spaceName}`);
return {
content: [{
type: "text" as const,
text: JSON.stringify({ messages, nextPageToken: res.data.nextPageToken })
}]
};
}
return {
content: [{
type: "text" as const,
text: JSON.stringify({ messages, nextPageToken: res.data.nextPageToken })
}]
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
logToFile(`Error during chat.getMessages: ${errorMessage}`);
Expand All @@ -263,6 +251,7 @@ export class ChatService {
}
}


public sendDm = async ({ email, message }: { email: string, message: string }) => {
logToFile(`chat.sendDm called with: email=${email}, message=${message}`);
try {
Expand Down