Skip to content

Commit 1357db6

Browse files
authored
Merge branch 'main' into group-dls-part-2
2 parents 26f599d + 62efe56 commit 1357db6

4 files changed

Lines changed: 210 additions & 92 deletions

File tree

frontend/components/cloud-picker/file-item.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,24 @@ const getMimeTypeLabel = (mimeType: string) => {
3131
"application/vnd.google-apps.folder": "Folder",
3232
"application/pdf": "PDF",
3333
"text/plain": "Text",
34+
"text/csv": "CSV",
35+
"text/html": "HTML",
36+
"application/xml": "XML",
37+
"application/json": "JSON",
38+
"application/msword": "Word Doc",
39+
"application/vnd.ms-excel": "Excel",
40+
"application/vnd.ms-powerpoint": "PowerPoint",
3441
"application/vnd.openxmlformats-officedocument.wordprocessingml.document":
3542
"Word Doc",
43+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
44+
"Excel",
3645
"application/vnd.openxmlformats-officedocument.presentationml.presentation":
3746
"PowerPoint",
47+
"application/octet-stream": "File",
48+
"image/jpeg": "JPEG",
49+
"image/png": "PNG",
50+
"image/gif": "GIF",
51+
"image/svg+xml": "SVG",
3852
};
3953

4054
return typeMap[mimeType] || mimeType?.split("/").pop() || "Document";

frontend/components/cloud-picker/provider-handlers.ts

Lines changed: 104 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -159,17 +159,20 @@ export class OneDriveHandler {
159159
private clientId: string;
160160
private provider: CloudProvider;
161161
private baseUrl?: string;
162+
private onPickerStateChange?: (isOpen: boolean) => void;
162163

163164
constructor(
164165
accessToken: string,
165166
clientId: string,
166167
provider: CloudProvider = "onedrive",
167168
baseUrl?: string,
169+
onPickerStateChange?: (isOpen: boolean) => void,
168170
) {
169171
this.accessToken = accessToken;
170172
this.clientId = clientId;
171173
this.provider = provider;
172174
this.baseUrl = baseUrl;
175+
this.onPickerStateChange = onPickerStateChange;
173176
}
174177

175178
async loadPickerApi(): Promise<boolean> {
@@ -206,57 +209,111 @@ export class OneDriveHandler {
206209
console.log("OneDrive picker success callback:", response);
207210
if (!response || !response.value) {
208211
console.warn("OneDrive picker returned no value");
212+
this.onPickerStateChange?.(false);
209213
return;
210214
}
211215

212-
const newFiles: CloudFile[] =
213-
response.value?.map((item: any) => {
214-
// Extract mimeType from file object or infer from name
215-
let mimeType = item.file?.mimeType;
216-
if (!mimeType && item.name) {
217-
// Infer from extension if mimeType not provided
218-
const ext = item.name.split(".").pop()?.toLowerCase();
219-
const mimeTypes: { [key: string]: string } = {
220-
pdf: "application/pdf",
221-
doc: "application/msword",
222-
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
223-
xls: "application/vnd.ms-excel",
224-
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
225-
ppt: "application/vnd.ms-powerpoint",
226-
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
227-
txt: "text/plain",
228-
csv: "text/csv",
229-
json: "application/json",
230-
xml: "application/xml",
231-
html: "text/html",
232-
jpg: "image/jpeg",
233-
jpeg: "image/jpeg",
234-
png: "image/png",
235-
gif: "image/gif",
236-
svg: "image/svg+xml",
237-
};
238-
mimeType = mimeTypes[ext || ""] || "application/octet-stream";
239-
}
216+
// v7.2 action:"query" returns stubs with only id/endpoint/parentReference.
217+
// Enrich each item by fetching full metadata from the Graph API.
218+
const enrichItems = async () => {
219+
const enriched = await Promise.all(
220+
response.value.map(async (item: any) => {
221+
const driveId =
222+
item.parentReference?.driveId || item.id?.split("!")[0];
223+
const itemId = item.id;
240224

241-
return {
242-
id: item.id,
243-
name: item.name || `${this.getProviderName()} File`,
244-
mimeType: mimeType || "application/octet-stream",
245-
webUrl: item.webUrl || "",
246-
downloadUrl: item["@microsoft.graph.downloadUrl"] || "",
247-
size: item.size,
248-
modifiedTime: item.lastModifiedDateTime,
249-
isFolder: !!item.folder,
250-
};
251-
}) || [];
225+
if (driveId && itemId) {
226+
try {
227+
const url = `https://graph.microsoft.com/v1.0/drives/${driveId}/items/${itemId}`;
228+
const res = await fetch(url, {
229+
headers: { Authorization: `Bearer ${this.accessToken}` },
230+
});
231+
if (res.ok) {
232+
const meta = await res.json();
233+
console.log(
234+
"OneDrive enriched metadata:",
235+
meta.name,
236+
meta.file?.mimeType,
237+
);
238+
239+
let mimeType = meta.file?.mimeType;
240+
if (!mimeType && meta.name) {
241+
const ext = meta.name.split(".").pop()?.toLowerCase();
242+
const mimeTypes: { [key: string]: string } = {
243+
pdf: "application/pdf",
244+
doc: "application/msword",
245+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
246+
xls: "application/vnd.ms-excel",
247+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
248+
ppt: "application/vnd.ms-powerpoint",
249+
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
250+
txt: "text/plain",
251+
csv: "text/csv",
252+
json: "application/json",
253+
xml: "application/xml",
254+
html: "text/html",
255+
jpg: "image/jpeg",
256+
jpeg: "image/jpeg",
257+
png: "image/png",
258+
gif: "image/gif",
259+
svg: "image/svg+xml",
260+
};
261+
mimeType =
262+
mimeTypes[ext || ""] || "application/octet-stream";
263+
}
252264

253-
onFileSelected(newFiles);
265+
return {
266+
id: meta.id,
267+
name: meta.name || `${this.getProviderName()} File`,
268+
mimeType: mimeType || "application/octet-stream",
269+
webUrl: meta.webUrl || "",
270+
downloadUrl: meta["@microsoft.graph.downloadUrl"] || "",
271+
size: meta.size,
272+
modifiedTime: meta.lastModifiedDateTime,
273+
isFolder: !!meta.folder,
274+
} as CloudFile;
275+
} else {
276+
console.warn(
277+
"Graph API metadata fetch failed:",
278+
res.status,
279+
await res.text(),
280+
);
281+
}
282+
} catch (e) {
283+
console.warn("Graph API metadata fetch error:", e);
284+
}
285+
}
286+
287+
// Fallback: use stub data if Graph call fails
288+
return {
289+
id: item.id,
290+
name: item.name || `${this.getProviderName()} File`,
291+
mimeType: "application/octet-stream",
292+
webUrl: item.webUrl || "",
293+
downloadUrl: item["@microsoft.graph.downloadUrl"] || "",
294+
size: item.size,
295+
modifiedTime: item.lastModifiedDateTime,
296+
isFolder: !!item.folder,
297+
} as CloudFile;
298+
}),
299+
);
300+
301+
onFileSelected(enriched);
302+
this.onPickerStateChange?.(false);
303+
};
304+
305+
enrichItems().catch((e) => {
306+
console.error("Failed to enrich OneDrive items:", e);
307+
this.onPickerStateChange?.(false);
308+
});
254309
},
255310
cancel: () => {
256311
console.log("Picker cancelled");
312+
this.onPickerStateChange?.(false);
257313
},
258314
error: (error: any) => {
259315
console.error("Picker error callback:", error);
316+
this.onPickerStateChange?.(false);
260317
},
261318
});
262319
}
@@ -310,7 +367,13 @@ export const createProviderHandler = (
310367
if (!clientId) {
311368
throw new Error("Client ID required for OneDrive");
312369
}
313-
return new OneDriveHandler(accessToken, clientId, provider, baseUrl);
370+
return new OneDriveHandler(
371+
accessToken,
372+
clientId,
373+
provider,
374+
baseUrl,
375+
onPickerStateChange,
376+
);
314377
default:
315378
throw new Error(`Unsupported provider: ${provider}`);
316379
}

src/connectors/onedrive/connector.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -737,7 +737,7 @@ async def _fetch_item_metadata(self, file_id: str) -> dict[str, Any] | None:
737737
# Try: /drives/{driveId}/items/{itemId} with full item ID (including 's' prefix)
738738
logger.info(f"Trying drives endpoint: /drives/{drive_id}/items/{item_id}")
739739
try:
740-
url = f"{self._graph_base_url}/drives/{drive_id}/items/{item_id}"
740+
url = f"{self._graph_base_url}/drives/{drive_id}/items/{file_id}"
741741
response = await self._make_graph_request(url, params=params)
742742
if response.status_code == 200:
743743
return response.json()
@@ -755,7 +755,7 @@ async def _fetch_item_metadata(self, file_id: str) -> dict[str, Any] | None:
755755
f"Trying drives endpoint without 's' prefix: /drives/{drive_id}/items/{clean_item_id}"
756756
)
757757
try:
758-
url = f"{self._graph_base_url}/drives/{drive_id}/items/{clean_item_id}"
758+
url = f"{self._graph_base_url}/drives/{drive_id}/items/{clean_file_id}"
759759
response = await self._make_graph_request(url, params=params)
760760
if response.status_code == 200:
761761
return response.json()
@@ -811,8 +811,8 @@ async def _download_file_content(self, file_id: str) -> bytes:
811811
if content is not None:
812812
return content
813813

814-
# Try drives endpoint for driveId!itemId format (including the 's' prefix)
815-
url = f"{self._graph_base_url}/drives/{drive_id}/items/{item_id}/content"
814+
# Try drives endpoint for driveId!itemId format
815+
url = f"{self._graph_base_url}/drives/{drive_id}/items/{file_id}/content"
816816
logger.info(f"Downloading via drives endpoint: {url}")
817817
else:
818818
url = f"{self._graph_base_url}/me/drive/items/{file_id}/content"

0 commit comments

Comments
 (0)