Initial commit: OL Rescue Outlook add-in (New Outlook + Graph)
Made-with: Cursor
This commit is contained in:
127
src/graph.ts
Normal file
127
src/graph.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
const GRAPH = "https://graph.microsoft.com/v1.0";
|
||||
|
||||
function escapeODataString(s: string): string {
|
||||
return s.replace(/'/g, "''");
|
||||
}
|
||||
|
||||
export interface GraphMessage {
|
||||
id: string;
|
||||
subject?: string;
|
||||
}
|
||||
|
||||
export interface GraphMailFolder {
|
||||
id: string;
|
||||
displayName: string;
|
||||
}
|
||||
|
||||
async function graphJson<T>(token: string, pathOrUrl: string, init?: RequestInit): Promise<T> {
|
||||
const url = pathOrUrl.startsWith("https://") ? pathOrUrl : `${GRAPH}${pathOrUrl}`;
|
||||
const res = await fetch(url, {
|
||||
...init,
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
...(init?.headers ?? {}),
|
||||
},
|
||||
});
|
||||
if (!res.ok) {
|
||||
const text = await res.text();
|
||||
throw new Error(`Graph ${res.status}: ${text || res.statusText}`);
|
||||
}
|
||||
return res.json() as Promise<T>;
|
||||
}
|
||||
|
||||
export async function listInboxChildFolders(token: string): Promise<GraphMailFolder[]> {
|
||||
const out: GraphMailFolder[] = [];
|
||||
let next: string | null =
|
||||
"/me/mailFolders/inbox/childFolders?$select=id,displayName&$top=999";
|
||||
|
||||
while (next) {
|
||||
const page = await graphJson<{ value: GraphMailFolder[]; "@odata.nextLink"?: string }>(
|
||||
token,
|
||||
next,
|
||||
);
|
||||
out.push(...page.value);
|
||||
next = page["@odata.nextLink"] ?? null;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
export async function findChildFolderByName(
|
||||
token: string,
|
||||
displayName: string,
|
||||
): Promise<GraphMailFolder | undefined> {
|
||||
const folders = await listInboxChildFolders(token);
|
||||
return folders.find((f) => f.displayName === displayName);
|
||||
}
|
||||
|
||||
export async function createInboxChildFolder(
|
||||
token: string,
|
||||
displayName: string,
|
||||
): Promise<GraphMailFolder> {
|
||||
return graphJson<GraphMailFolder>(token, "/me/mailFolders/inbox/childFolders", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ displayName }),
|
||||
});
|
||||
}
|
||||
|
||||
export async function findOrCreateInboxChildFolder(
|
||||
token: string,
|
||||
displayName: string,
|
||||
): Promise<GraphMailFolder> {
|
||||
const existing = await findChildFolderByName(token, displayName);
|
||||
if (existing) {
|
||||
return existing;
|
||||
}
|
||||
return createInboxChildFolder(token, displayName);
|
||||
}
|
||||
|
||||
async function listMessagesMatchingFilter(
|
||||
token: string,
|
||||
filter: string,
|
||||
): Promise<GraphMessage[]> {
|
||||
const out: GraphMessage[] = [];
|
||||
let next: string | null =
|
||||
`/me/messages?$filter=${encodeURIComponent(filter)}&$select=id,subject&$top=999`;
|
||||
|
||||
while (next) {
|
||||
const page = await graphJson<{ value: GraphMessage[]; "@odata.nextLink"?: string }>(
|
||||
token,
|
||||
next,
|
||||
);
|
||||
out.push(...page.value);
|
||||
next = page["@odata.nextLink"] ?? null;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds messages whose subject or preview contains "TrackingID#<id>".
|
||||
* Falls back to subject-only filter if the tenant rejects bodyPreview in $filter.
|
||||
*/
|
||||
export async function findMessagesWithTrackingId(
|
||||
token: string,
|
||||
trackingId: string,
|
||||
): Promise<GraphMessage[]> {
|
||||
const needle = `TrackingID#${trackingId}`;
|
||||
const q = escapeODataString(needle);
|
||||
const combined = `(contains(subject,'${q}') or contains(bodyPreview,'${q}'))`;
|
||||
const subjectOnly = `contains(subject,'${q}')`;
|
||||
try {
|
||||
return await listMessagesMatchingFilter(token, combined);
|
||||
} catch {
|
||||
return listMessagesMatchingFilter(token, subjectOnly);
|
||||
}
|
||||
}
|
||||
|
||||
export async function moveMessageToFolder(
|
||||
token: string,
|
||||
messageId: string,
|
||||
destinationFolderId: string,
|
||||
): Promise<void> {
|
||||
await graphJson<unknown>(token, `/me/messages/${encodeURIComponent(messageId)}/move`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ destinationId: destinationFolderId }),
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user