diff --git a/gemini-extension.json b/gemini-extension.json index c154e3c..c6e94c3 100644 --- a/gemini-extension.json +++ b/gemini-extension.json @@ -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}" } } -} +} \ No newline at end of file diff --git a/workspace-server/src/__tests__/services/ChatService.test.ts b/workspace-server/src/__tests__/services/ChatService.test.ts index d75287d..1ffbe17 100644 --- a/workspace-server/src/__tests__/services/ChatService.test.ts +++ b/workspace-server/src/__tests__/services/ChatService.test.ts @@ -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', () => { diff --git a/workspace-server/src/index.ts b/workspace-server/src/index.ts index 83af4a8..31a0260 100644 --- a/workspace-server/src/index.ts +++ b/workspace-server/src/index.ts @@ -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 diff --git a/workspace-server/src/services/ChatService.ts b/workspace-server/src/services/ChatService.ts index 2142efe..530449e 100644 --- a/workspace-server/src/services/ChatService.ts +++ b/workspace-server/src/services/ChatService.ts @@ -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({ @@ -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}`); @@ -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 {