inbox-zero
inbox-zero copied to clipboard
Create email provider adapter layer (for Outlook)
This should only be done after #271 and #272 are completed.
Create a common interface for email operations that works with both Gmail and Outlook.
Example Implementation
This was written by an LLM to give you an idea of how this could be approached:
// types.ts
interface EmailMessage {
id: string;
threadId: string; // Gmail threadId or Outlook conversationId
subject: string;
snippet: string;
date: Date;
}
interface EmailProvider {
// Core methods we need for MVP
getMessage(messageId: string): Promise<EmailMessage>;
listMessages(options: {
folderId?: string, // Label in Gmail, Folder in Outlook
pageToken?: string,
maxResults?: number
}): Promise<{
messages: EmailMessage[],
nextPageToken?: string
}>;
// Add more as needed
}
// gmail-provider.ts
class GmailProvider implements EmailProvider {
constructor(private gmail: gmail_v1.Gmail) {}
async getMessage(messageId: string): Promise<EmailMessage> {
const msg = await this.gmail.users.messages.get({
userId: 'me',
id: messageId
});
return {
id: msg.data.id!,
threadId: msg.data.threadId!,
subject: getSubject(msg.data), // Helper to parse Gmail headers
snippet: msg.data.snippet!,
date: new Date(getDate(msg.data)) // Helper to parse Gmail headers
};
}
async listMessages(options: {
folderId?: string,
pageToken?: string,
maxResults?: number
}) {
const response = await this.gmail.users.messages.list({
userId: 'me',
labelIds: options.folderId ? [options.folderId] : undefined,
maxResults: options.maxResults,
pageToken: options.pageToken
});
const messages = await Promise.all(
response.data.messages!.map(m => this.getMessage(m.id!))
);
return {
messages,
nextPageToken: response.data.nextPageToken
};
}
}
// outlook-provider.ts
class OutlookProvider implements EmailProvider {
constructor(private graph: Client) {}
async getMessage(messageId: string): Promise<EmailMessage> {
const msg = await this.graph.api(`/me/messages/${messageId}`)
.select('id,conversationId,subject,bodyPreview,receivedDateTime')
.get();
return {
id: msg.id,
threadId: msg.conversationId, // Using conversationId as threadId
subject: msg.subject,
snippet: msg.bodyPreview,
date: new Date(msg.receivedDateTime)
};
}
async listMessages(options: {
folderId?: string,
pageToken?: string,
maxResults?: number
}) {
let request = this.graph.api('/me/messages');
if (options.folderId) {
request = this.graph.api(`/me/mailFolders/${options.folderId}/messages`);
}
const response = await request
.select('id,conversationId,subject,bodyPreview,receivedDateTime')
.top(options.maxResults || 50)
.skipToken(options.pageToken)
.get();
return {
messages: response.value.map(msg => ({
id: msg.id,
threadId: msg.conversationId,
subject: msg.subject,
snippet: msg.bodyPreview,
date: new Date(msg.receivedDateTime)
})),
nextPageToken: response['@odata.nextLink']
};
}
}
// factory.ts
function createEmailProvider(type: 'gmail' | 'outlook', client: any): EmailProvider {
if (type === 'gmail') {
return new GmailProvider(client);
}
return new OutlookProvider(client);
}
// Usage example
const provider = createEmailProvider(session.provider as 'gmail' | 'outlook', client);
const messages = await provider.listMessages({ maxResults: 10 });
Tasks
- [ ] Create EmailMessage and EmailProvider interfaces
- [ ] Implement GmailProvider
- [ ] Implement OutlookProvider
- [ ] Add factory function
- [ ] Test basic operations (get/list messages)
- [ ] Add webhook handling
Done when
- Can switch between Gmail/Outlook without changing app code
- Core email operations working with both providers
- Webhook endpoints handling both providers
Please also support normal standards like json imap Gmail and MS also support imap...
Being worked on here: https://github.com/elie222/inbox-zero/pull/493