Merge pull request #421 from langflow-ai/fix-gdrive-connector

fix: Improve the Google Drive / Sharepoint / OneDrive connector's validation and sync
This commit is contained in:
pushkala-datastax 2025-11-19 14:22:32 -08:00 committed by GitHub
commit c7a8e89132
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 325 additions and 89 deletions

View file

@ -119,9 +119,9 @@ function AuthCallbackContent() {
localStorage.removeItem("connecting_connector_type");
localStorage.removeItem("auth_purpose");
// Redirect to connectors page with success indicator
// Redirect to settings page with success indicator
setTimeout(() => {
router.push("/connectors?oauth_success=true");
router.push("/settings?oauth_success=true");
}, 2000);
}
} else {
@ -207,13 +207,13 @@ function AuthCallbackContent() {
</div>
<Button
onClick={() =>
router.push(isAppAuth ? "/login" : "/connectors")
router.push(isAppAuth ? "/login" : "/settings")
}
variant="outline"
className="w-full"
>
<ArrowLeft className="h-4 w-4 mr-2" />
{isAppAuth ? "Back to Login" : "Back to Connectors"}
{isAppAuth ? "Back to Login" : "Back to Settings"}
</Button>
</div>
)}
@ -223,7 +223,7 @@ function AuthCallbackContent() {
<p className="text-sm text-green-600">
{isAppAuth
? "Redirecting you to the app..."
: "Redirecting to connectors..."}
: "Redirecting to settings..."}
</p>
</div>
</div>

View file

@ -366,7 +366,7 @@ export default function UploadProviderPage() {
Back
</Button>
<Tooltip>
<TooltipTrigger>
<TooltipTrigger asChild>
<Button
className="bg-foreground text-background hover:bg-foreground/90 font-semibold"
variant={!hasSelectedFiles ? "secondary" : undefined}

View file

@ -196,20 +196,45 @@ export class OneDriveHandler {
},
success: (response: any) => {
const newFiles: CloudFile[] =
response.value?.map((item: any, index: number) => ({
id: item.id,
name:
item.name ||
`${this.getProviderName()} File ${index + 1} (${item.id.slice(
-8,
)})`,
mimeType: item.file?.mimeType || "application/octet-stream",
webUrl: item.webUrl || "",
downloadUrl: item["@microsoft.graph.downloadUrl"] || "",
size: item.size,
modifiedTime: item.lastModifiedDateTime,
isFolder: !!item.folder,
})) || [];
response.value?.map((item: any) => {
// Extract mimeType from file object or infer from name
let mimeType = item.file?.mimeType;
if (!mimeType && item.name) {
// Infer from extension if mimeType not provided
const ext = item.name.split('.').pop()?.toLowerCase();
const mimeTypes: { [key: string]: string } = {
pdf: 'application/pdf',
doc: 'application/msword',
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
xls: 'application/vnd.ms-excel',
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
ppt: 'application/vnd.ms-powerpoint',
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
txt: 'text/plain',
csv: 'text/csv',
json: 'application/json',
xml: 'application/xml',
html: 'text/html',
jpg: 'image/jpeg',
jpeg: 'image/jpeg',
png: 'image/png',
gif: 'image/gif',
svg: 'image/svg+xml',
};
mimeType = mimeTypes[ext || ''] || 'application/octet-stream';
}
return {
id: item.id,
name: item.name || `${this.getProviderName()} File`,
mimeType: mimeType || "application/octet-stream",
webUrl: item.webUrl || "",
downloadUrl: item["@microsoft.graph.downloadUrl"] || "",
size: item.size,
modifiedTime: item.lastModifiedDateTime,
isFolder: !!item.folder,
};
}) || [];
onFileSelected(newFiles);
},

View file

@ -8,7 +8,7 @@ const OneDriveLogo = ({ className }: { className?: string }) => (
className={className}
>
<title>OneDrive Logo</title>
<g clip-path="url(#clip0_3016_367)">
<g clipPath="url(#clip0_3016_367)">
<path
d="M5.2316 2.32803C2.88332 2.3281 1.128 4.25034 0.99585 6.39175C1.07765 6.85315 1.34653 7.7643 1.76759 7.71751C2.29391 7.65902 3.61947 7.71751 4.75008 5.67068C5.57599 4.17546 7.27498 2.328 5.2316 2.32803Z"
fill="url(#paint0_radial_3016_367)"
@ -20,7 +20,7 @@ const OneDriveLogo = ({ className }: { className?: string }) => (
<path
d="M4.68864 3.12741C3.89927 4.37718 2.83674 6.16798 2.47813 6.7315C2.05185 7.40136 0.922937 7.11678 1.01646 6.15663C1.00724 6.23457 1.00016 6.31315 0.995274 6.39226C0.840839 8.89029 2.82143 10.9648 5.28604 10.9648C8.00238 10.9648 14.4806 7.58038 13.825 4.18931C13.134 2.19599 11.1918 0.766266 8.99072 0.766266C6.78965 0.766266 5.37899 2.03436 4.68864 3.12741Z"
fill="url(#paint2_radial_3016_367)"
fill-opacity="0.4"
fillOpacity="0.4"
/>
<path
d="M4.68864 3.12741C3.89927 4.37718 2.83674 6.16798 2.47813 6.7315C2.05185 7.40136 0.922937 7.11678 1.01646 6.15663C1.00724 6.23457 1.00016 6.31315 0.995274 6.39226C0.840839 8.89029 2.82143 10.9648 5.28604 10.9648C8.00238 10.9648 14.4806 7.58038 13.825 4.18931C13.134 2.19599 11.1918 0.766266 8.99072 0.766266C6.78965 0.766266 5.37899 2.03436 4.68864 3.12741Z"
@ -29,12 +29,12 @@ const OneDriveLogo = ({ className }: { className?: string }) => (
<path
d="M4.68864 3.12741C3.89927 4.37718 2.83674 6.16798 2.47813 6.7315C2.05185 7.40136 0.922937 7.11678 1.01646 6.15663C1.00724 6.23457 1.00016 6.31315 0.995274 6.39226C0.840839 8.89029 2.82143 10.9648 5.28604 10.9648C8.00238 10.9648 14.4806 7.58038 13.825 4.18931C13.134 2.19599 11.1918 0.766266 8.99072 0.766266C6.78965 0.766266 5.37899 2.03436 4.68864 3.12741Z"
fill="url(#paint4_radial_3016_367)"
fill-opacity="0.6"
fillOpacity="0.6"
/>
<path
d="M4.68864 3.12741C3.89927 4.37718 2.83674 6.16798 2.47813 6.7315C2.05185 7.40136 0.922937 7.11678 1.01646 6.15663C1.00724 6.23457 1.00016 6.31315 0.995274 6.39226C0.840839 8.89029 2.82143 10.9648 5.28604 10.9648C8.00238 10.9648 14.4806 7.58038 13.825 4.18931C13.134 2.19599 11.1918 0.766266 8.99072 0.766266C6.78965 0.766266 5.37899 2.03436 4.68864 3.12741Z"
fill="url(#paint5_radial_3016_367)"
fill-opacity="0.9"
fillOpacity="0.9"
/>
<path
d="M5.24634 10.9659C5.24634 10.9659 11.7322 10.9786 12.8323 10.9786C14.8288 10.9786 16.3467 9.34866 16.3468 7.44669C16.3468 5.54468 14.7983 3.92459 12.8323 3.92459C10.8663 3.92459 9.73412 5.39542 8.88374 7.00089C7.8873 8.88221 6.61615 10.9433 5.24634 10.9659Z"
@ -43,12 +43,12 @@ const OneDriveLogo = ({ className }: { className?: string }) => (
<path
d="M5.24634 10.9659C5.24634 10.9659 11.7322 10.9786 12.8323 10.9786C14.8288 10.9786 16.3467 9.34866 16.3468 7.44669C16.3468 5.54468 14.7983 3.92459 12.8323 3.92459C10.8663 3.92459 9.73412 5.39542 8.88374 7.00089C7.8873 8.88221 6.61615 10.9433 5.24634 10.9659Z"
fill="url(#paint7_radial_3016_367)"
fill-opacity="0.4"
fillOpacity="0.4"
/>
<path
d="M5.24634 10.9659C5.24634 10.9659 11.7322 10.9786 12.8323 10.9786C14.8288 10.9786 16.3467 9.34866 16.3468 7.44669C16.3468 5.54468 14.7983 3.92459 12.8323 3.92459C10.8663 3.92459 9.73412 5.39542 8.88374 7.00089C7.8873 8.88221 6.61615 10.9433 5.24634 10.9659Z"
fill="url(#paint8_radial_3016_367)"
fill-opacity="0.9"
fillOpacity="0.9"
/>
</g>
<defs>
@ -60,8 +60,8 @@ const OneDriveLogo = ({ className }: { className?: string }) => (
gradientUnits="userSpaceOnUse"
gradientTransform="translate(1.28709 2.88928) rotate(50.1526) scale(4.84121 8.03004)"
>
<stop stop-color="#4894FE" />
<stop offset="0.695072" stop-color="#0934B3" />
<stop stopColor="#4894FE" />
<stop offset="0.695072" stopColor="#0934B3" />
</radialGradient>
<radialGradient
id="paint1_radial_3016_367"
@ -71,8 +71,8 @@ const OneDriveLogo = ({ className }: { className?: string }) => (
gradientUnits="userSpaceOnUse"
gradientTransform="translate(14.2836 -2.68456) rotate(130.923) scale(20.8177 15.4261)"
>
<stop offset="0.165327" stop-color="#23C0FE" />
<stop offset="0.534" stop-color="#1C91FF" />
<stop offset="0.165327" stopColor="#23C0FE" />
<stop offset="0.534" stopColor="#1C91FF" />
</radialGradient>
<radialGradient
id="paint2_radial_3016_367"
@ -82,8 +82,8 @@ const OneDriveLogo = ({ className }: { className?: string }) => (
gradientUnits="userSpaceOnUse"
gradientTransform="translate(4.42852 3.16495) rotate(-139.986) scale(4.23243 9.68892)"
>
<stop stop-color="white" />
<stop offset="0.660528" stop-color="#ADC0FF" stop-opacity="0" />
<stop stopColor="white" />
<stop offset="0.660528" stopColor="#ADC0FF" stopOpacity="0" />
</radialGradient>
<radialGradient
id="paint3_radial_3016_367"
@ -93,8 +93,8 @@ const OneDriveLogo = ({ className }: { className?: string }) => (
gradientUnits="userSpaceOnUse"
gradientTransform="translate(9.03076 8.16737) rotate(-139.764) scale(4.77056 7.24512)"
>
<stop stop-color="#033ACC" />
<stop offset="1" stop-color="#368EFF" stop-opacity="0" />
<stop stopColor="#033ACC" />
<stop offset="1" stopColor="#368EFF" stopOpacity="0" />
</radialGradient>
<radialGradient
id="paint4_radial_3016_367"
@ -104,8 +104,8 @@ const OneDriveLogo = ({ className }: { className?: string }) => (
gradientUnits="userSpaceOnUse"
gradientTransform="translate(4.14837 0.44361) rotate(66.5713) scale(10.4677 11.3005)"
>
<stop offset="0.592618" stop-color="#3464E3" stop-opacity="0" />
<stop offset="1" stop-color="#033ACC" />
<stop offset="0.592618" stopColor="#3464E3" stopOpacity="0" />
<stop offset="1" stopColor="#033ACC" />
</radialGradient>
<radialGradient
id="paint5_radial_3016_367"
@ -115,8 +115,8 @@ const OneDriveLogo = ({ className }: { className?: string }) => (
gradientUnits="userSpaceOnUse"
gradientTransform="translate(14.1157 -1.59739) rotate(135) scale(15.3977 24.123)"
>
<stop stop-color="#4BFDE8" />
<stop offset="0.543937" stop-color="#4BFDE8" stop-opacity="0" />
<stop stopColor="#4BFDE8" />
<stop offset="0.543937" stopColor="#4BFDE8" stopOpacity="0" />
</radialGradient>
<linearGradient
id="paint6_linear_3016_367"
@ -126,8 +126,8 @@ const OneDriveLogo = ({ className }: { className?: string }) => (
y2="4.00825"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#0086FF" />
<stop offset="0.49" stop-color="#00BBFF" />
<stop stopColor="#0086FF" />
<stop offset="0.49" stopColor="#00BBFF" />
</linearGradient>
<radialGradient
id="paint7_radial_3016_367"
@ -137,8 +137,8 @@ const OneDriveLogo = ({ className }: { className?: string }) => (
gradientUnits="userSpaceOnUse"
gradientTransform="translate(7.16132 4.75417) rotate(21.6324) scale(6.97728 13.2126)"
>
<stop stop-color="white" />
<stop offset="0.785262" stop-color="white" stop-opacity="0" />
<stop stopColor="white" />
<stop offset="0.785262" stopColor="white" stopOpacity="0" />
</radialGradient>
<radialGradient
id="paint8_radial_3016_367"
@ -148,8 +148,8 @@ const OneDriveLogo = ({ className }: { className?: string }) => (
gradientUnits="userSpaceOnUse"
gradientTransform="translate(16.1298 3.37785) rotate(139.243) scale(9.56565 9.59808)"
>
<stop stop-color="#4BFDE8" />
<stop offset="0.584724" stop-color="#4BFDE8" stop-opacity="0" />
<stop stopColor="#4BFDE8" />
<stop offset="0.584724" stopColor="#4BFDE8" stopOpacity="0" />
</radialGradient>
<clipPath id="clip0_3016_367">
<rect

View file

@ -8,7 +8,7 @@ const SharePointLogo = ({ className }: { className?: string }) => (
className={className}
>
<title>SharePoint Logo</title>
<g clip-path="url(#clip0_3016_409)">
<g clipPath="url(#clip0_3016_409)">
<path
d="M6.1335 9.6C8.78446 9.6 10.9335 7.45096 10.9335 4.8C10.9335 2.14903 8.78446 0 6.1335 0C3.48254 0 1.3335 2.14903 1.3335 4.8C1.3335 7.45096 3.48254 9.6 6.1335 9.6Z"
fill="url(#paint0_linear_3016_409)"
@ -16,17 +16,17 @@ const SharePointLogo = ({ className }: { className?: string }) => (
<path
d="M6.1335 9.6C8.78446 9.6 10.9335 7.45096 10.9335 4.8C10.9335 2.14903 8.78446 0 6.1335 0C3.48254 0 1.3335 2.14903 1.3335 4.8C1.3335 7.45096 3.48254 9.6 6.1335 9.6Z"
fill="url(#paint1_radial_3016_409)"
fill-opacity="0.2"
fillOpacity="0.2"
/>
<path
d="M6.1335 9.6C8.78446 9.6 10.9335 7.45096 10.9335 4.8C10.9335 2.14903 8.78446 0 6.1335 0C3.48254 0 1.3335 2.14903 1.3335 4.8C1.3335 7.45096 3.48254 9.6 6.1335 9.6Z"
fill="url(#paint2_radial_3016_409)"
fill-opacity="0.31"
fillOpacity="0.31"
/>
<path
d="M6.1335 9.6C8.78446 9.6 10.9335 7.45096 10.9335 4.8C10.9335 2.14903 8.78446 0 6.1335 0C3.48254 0 1.3335 2.14903 1.3335 4.8C1.3335 7.45096 3.48254 9.6 6.1335 9.6Z"
fill="url(#paint3_radial_3016_409)"
fill-opacity="0.7"
fillOpacity="0.7"
/>
<path
d="M10.5117 12.8C12.7209 12.8 14.5117 11.0091 14.5117 8.8C14.5117 6.59088 12.7209 4.8 10.5117 4.8C8.3026 4.8 6.51172 6.59088 6.51172 8.8C6.51172 11.0091 8.3026 12.8 10.5117 12.8Z"
@ -35,12 +35,12 @@ const SharePointLogo = ({ className }: { className?: string }) => (
<path
d="M10.5117 12.8C12.7209 12.8 14.5117 11.0091 14.5117 8.8C14.5117 6.59088 12.7209 4.8 10.5117 4.8C8.3026 4.8 6.51172 6.59088 6.51172 8.8C6.51172 11.0091 8.3026 12.8 10.5117 12.8Z"
fill="url(#paint5_radial_3016_409)"
fill-opacity="0.5"
fillOpacity="0.5"
/>
<path
d="M10.5117 12.8C12.7209 12.8 14.5117 11.0091 14.5117 8.8C14.5117 6.59088 12.7209 4.8 10.5117 4.8C8.3026 4.8 6.51172 6.59088 6.51172 8.8C6.51172 11.0091 8.3026 12.8 10.5117 12.8Z"
fill="url(#paint6_radial_3016_409)"
fill-opacity="0.7"
fillOpacity="0.7"
/>
<path
d="M6.7335 16C8.61126 16 10.1335 14.4778 10.1335 12.6C10.1335 10.7222 8.61126 9.2 6.7335 9.2C4.85574 9.2 3.3335 10.7222 3.3335 12.6C3.3335 14.4778 4.85574 16 6.7335 16Z"
@ -49,7 +49,7 @@ const SharePointLogo = ({ className }: { className?: string }) => (
<path
d="M6.7335 16C8.61126 16 10.1335 14.4778 10.1335 12.6C10.1335 10.7222 8.61126 9.2 6.7335 9.2C4.85574 9.2 3.3335 10.7222 3.3335 12.6C3.3335 14.4778 4.85574 16 6.7335 16Z"
fill="url(#paint8_linear_3016_409)"
fill-opacity="0.32"
fillOpacity="0.32"
/>
<path
d="M5.23354 7.60001H1.43354C0.715575 7.60001 0.133545 8.18204 0.133545 8.90001V12.7C0.133545 13.418 0.715575 14 1.43354 14H5.23354C5.95151 14 6.53354 13.418 6.53354 12.7V8.90001C6.53354 8.18204 5.95151 7.60001 5.23354 7.60001Z"
@ -58,7 +58,7 @@ const SharePointLogo = ({ className }: { className?: string }) => (
<path
d="M5.23354 7.60001H1.43354C0.715575 7.60001 0.133545 8.18204 0.133545 8.90001V12.7C0.133545 13.418 0.715575 14 1.43354 14H5.23354C5.95151 14 6.53354 13.418 6.53354 12.7V8.90001C6.53354 8.18204 5.95151 7.60001 5.23354 7.60001Z"
fill="url(#paint10_radial_3016_409)"
fill-opacity="0.6"
fillOpacity="0.6"
/>
<path
d="M1.95581 11.8734L2.64917 11.523C2.72733 11.676 2.82929 11.7887 2.95505 11.8611C3.08249 11.9335 3.22185 11.9697 3.37309 11.9697C3.54133 11.9697 3.66965 11.9368 3.75801 11.871C3.84641 11.8036 3.89057 11.7024 3.89057 11.5675C3.89057 11.4622 3.84809 11.3733 3.76313 11.301C3.67817 11.2269 3.52777 11.171 3.31193 11.1332C2.90069 11.0608 2.60157 10.9341 2.41465 10.7531C2.22941 10.5722 2.13679 10.3468 2.13679 10.077C2.13679 9.74136 2.25915 9.4732 2.50387 9.27248C2.74857 9.0718 3.07145 8.97144 3.47253 8.97144C3.74273 8.97144 3.98065 9.02492 4.18629 9.13184C4.39189 9.23876 4.55505 9.39176 4.67569 9.59084L3.99765 9.92892C3.92285 9.81704 3.84213 9.73644 3.75549 9.68708C3.66881 9.63608 3.56005 9.61056 3.42917 9.61056C3.27285 9.61056 3.15389 9.64348 3.07233 9.70928C2.99245 9.77508 2.95249 9.86064 2.95249 9.96592C2.95249 10.0564 2.99073 10.1362 3.06721 10.2053C3.14537 10.2727 3.30173 10.3278 3.53625 10.3706C3.93053 10.443 4.22449 10.5746 4.41825 10.7654C4.61369 10.9546 4.71137 11.194 4.71137 11.4836C4.71137 11.8356 4.59497 12.1145 4.36217 12.3201C4.12933 12.5258 3.79713 12.6286 3.36545 12.6286C3.05277 12.6286 2.77065 12.5628 2.51916 12.4312C2.26935 12.2979 2.08157 12.112 1.95581 11.8734Z"
@ -78,9 +78,9 @@ const SharePointLogo = ({ className }: { className?: string }) => (
y2="9.6"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#00E3DF" />
<stop offset="0.410156" stop-color="#0097A8" />
<stop offset="1" stop-color="#007791" />
<stop stopColor="#00E3DF" />
<stop offset="0.410156" stopColor="#0097A8" />
<stop offset="1" stopColor="#007791" />
</linearGradient>
<radialGradient
id="paint1_radial_3016_409"
@ -90,9 +90,9 @@ const SharePointLogo = ({ className }: { className?: string }) => (
gradientUnits="userSpaceOnUse"
gradientTransform="translate(7.60222 10.9279) rotate(-112.448) scale(7.37044 13.2516)"
>
<stop offset="0.28573" stop-color="#003B5D" />
<stop offset="0.612265" stop-color="#004A6C" stop-opacity="0.688298" />
<stop offset="0.968041" stop-color="#006F94" stop-opacity="0" />
<stop offset="0.28573" stopColor="#003B5D" />
<stop offset="0.612265" stopColor="#004A6C" stopOpacity="0.688298" />
<stop offset="0.968041" stopColor="#006F94" stopOpacity="0" />
</radialGradient>
<radialGradient
id="paint2_radial_3016_409"
@ -102,9 +102,9 @@ const SharePointLogo = ({ className }: { className?: string }) => (
gradientUnits="userSpaceOnUse"
gradientTransform="translate(7.77166 8.81012) rotate(-112.063) scale(6.22076 11.1709)"
>
<stop offset="0.259744" stop-color="#002A42" />
<stop offset="0.612265" stop-color="#004261" stop-opacity="0.688298" />
<stop offset="0.968041" stop-color="#006F94" stop-opacity="0" />
<stop offset="0.259744" stopColor="#002A42" />
<stop offset="0.612265" stopColor="#004261" stopOpacity="0.688298" />
<stop offset="0.968041" stopColor="#006F94" stopOpacity="0" />
</radialGradient>
<radialGradient
id="paint3_radial_3016_409"
@ -114,8 +114,8 @@ const SharePointLogo = ({ className }: { className?: string }) => (
gradientUnits="userSpaceOnUse"
gradientTransform="translate(8.87294 0.508276) rotate(124.447) scale(5.20428)"
>
<stop stop-color="#78EDFF" />
<stop offset="1" stop-color="#2CCFCA" stop-opacity="0" />
<stop stopColor="#78EDFF" />
<stop offset="1" stopColor="#2CCFCA" stopOpacity="0" />
</radialGradient>
<linearGradient
id="paint4_linear_3016_409"
@ -125,9 +125,9 @@ const SharePointLogo = ({ className }: { className?: string }) => (
y2="12.8"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#00E3DF" />
<stop offset="0.476427" stop-color="#00A2B8" />
<stop offset="0.945063" stop-color="#00637C" />
<stop stopColor="#00E3DF" />
<stop offset="0.476427" stopColor="#00A2B8" />
<stop offset="0.945063" stopColor="#00637C" />
</linearGradient>
<radialGradient
id="paint5_radial_3016_409"
@ -137,9 +137,9 @@ const SharePointLogo = ({ className }: { className?: string }) => (
gradientUnits="userSpaceOnUse"
gradientTransform="translate(8.22004 12.1333) rotate(-70.8012) scale(4.94148 8.90348)"
>
<stop stop-color="#003B5D" />
<stop offset="0.492035" stop-color="#004C6C" stop-opacity="0.72" />
<stop offset="0.968041" stop-color="#007A86" stop-opacity="0" />
<stop stopColor="#003B5D" />
<stop offset="0.492035" stopColor="#004C6C" stopOpacity="0.72" />
<stop offset="0.968041" stopColor="#007A86" stopOpacity="0" />
</radialGradient>
<radialGradient
id="paint6_radial_3016_409"
@ -149,8 +149,8 @@ const SharePointLogo = ({ className }: { className?: string }) => (
gradientUnits="userSpaceOnUse"
gradientTransform="translate(12.7946 5.22356) rotate(124.447) scale(4.33692)"
>
<stop stop-color="#78EDFF" />
<stop offset="1" stop-color="#2CCFCA" stop-opacity="0" />
<stop stopColor="#78EDFF" />
<stop offset="1" stopColor="#2CCFCA" stopOpacity="0" />
</radialGradient>
<linearGradient
id="paint7_linear_3016_409"
@ -160,9 +160,9 @@ const SharePointLogo = ({ className }: { className?: string }) => (
y2="16.34"
gradientUnits="userSpaceOnUse"
>
<stop offset="0.0534989" stop-color="#75FFF6" />
<stop offset="0.51144" stop-color="#00C7D1" />
<stop offset="0.96002" stop-color="#0096AD" />
<stop offset="0.0534989" stopColor="#75FFF6" />
<stop offset="0.51144" stopColor="#00C7D1" />
<stop offset="0.96002" stopColor="#0096AD" />
</linearGradient>
<linearGradient
id="paint8_linear_3016_409"
@ -172,9 +172,9 @@ const SharePointLogo = ({ className }: { className?: string }) => (
y2="13.4503"
gradientUnits="userSpaceOnUse"
>
<stop offset="0.259744" stop-color="#0E5A5D" />
<stop offset="0.535716" stop-color="#126C6B" stop-opacity="0.688298" />
<stop offset="0.968041" stop-color="#1C948A" stop-opacity="0" />
<stop offset="0.259744" stopColor="#0E5A5D" />
<stop offset="0.535716" stopColor="#126C6B" stopOpacity="0.688298" />
<stop offset="0.968041" stopColor="#1C948A" stopOpacity="0" />
</linearGradient>
<radialGradient
id="paint9_radial_3016_409"
@ -184,8 +184,8 @@ const SharePointLogo = ({ className }: { className?: string }) => (
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0.133545 7.60001) rotate(45) scale(9.05096)"
>
<stop offset="0.0625" stop-color="#00B6BD" />
<stop offset="0.890131" stop-color="#00495C" />
<stop offset="0.0625" stopColor="#00B6BD" />
<stop offset="0.890131" stopColor="#00495C" />
</radialGradient>
<radialGradient
id="paint10_radial_3016_409"
@ -195,8 +195,8 @@ const SharePointLogo = ({ className }: { className?: string }) => (
gradientUnits="userSpaceOnUse"
gradientTransform="translate(3.33354 11.44) rotate(90) scale(4.48 5.1)"
>
<stop offset="0.566964" stop-color="#1E8581" stop-opacity="0" />
<stop offset="0.973806" stop-color="#1ECBE6" />
<stop offset="0.566964" stopColor="#1E8581" stopOpacity="0" />
<stop offset="0.973806" stopColor="#1ECBE6" />
</radialGradient>
<clipPath id="clip0_3016_409">
<rect

View file

@ -166,6 +166,10 @@ class GoogleDriveConnector(BaseConnector):
# -------------------------
# Helpers
# -------------------------
def _clear_shortcut_cache(self) -> None:
"""Clear the shortcut resolution cache to prevent stale data."""
self._shortcut_cache.clear()
@property
def _drives_get_flags(self) -> Dict[str, Any]:
"""
@ -208,6 +212,10 @@ class GoogleDriveConnector(BaseConnector):
if target_id in self._shortcut_cache:
return self._shortcut_cache[target_id]
if self.service is None:
logger.warning("Cannot resolve shortcut - service not initialized")
return file_obj
try:
meta = (
self.service.files()
@ -231,6 +239,11 @@ class GoogleDriveConnector(BaseConnector):
"""
List immediate children of a folder.
"""
if self.service is None:
raise RuntimeError(
"Google Drive service is not initialized. Please authenticate first."
)
query = f"'{folder_id}' in parents and trashed = false"
page_token = None
results: List[Dict[str, Any]] = []
@ -332,6 +345,9 @@ class GoogleDriveConnector(BaseConnector):
- items inside folder_ids (with optional recursion)
Shortcuts are resolved to their targets automatically.
"""
# Clear shortcut cache to ensure fresh data
self._clear_shortcut_cache()
seen: Set[str] = set()
items: List[Dict[str, Any]] = []
folders_to_expand: List[str] = []
@ -374,6 +390,10 @@ class GoogleDriveConnector(BaseConnector):
# - OR default to entire drive.
# Here we choose to require explicit selection:
if not self.cfg.file_ids and not self.cfg.folder_ids:
logger.warning(
"No file_ids or folder_ids specified - returning empty result. "
"Explicit selection is required."
)
return []
items = self._filter_by_mime(items)
@ -383,6 +403,16 @@ class GoogleDriveConnector(BaseConnector):
for m in items
if m.get("mimeType") != "application/vnd.google-apps.folder"
]
# Log a warning if we ended up with no files after expansion/filtering
if not items and (self.cfg.file_ids or self.cfg.folder_ids):
logger.warning(
f"No files found after expanding and filtering. "
f"file_ids={self.cfg.file_ids}, folder_ids={self.cfg.folder_ids}. "
f"This could mean: (1) folders are empty, (2) all files were filtered by mime types, "
f"or (3) permissions prevent access to the files."
)
return items
# -------------------------
@ -416,6 +446,11 @@ class GoogleDriveConnector(BaseConnector):
Download bytes for a given file (exporting if Google-native).
Raises ValueError if the item is a folder (folders cannot be downloaded).
"""
if self.service is None:
raise RuntimeError(
"Google Drive service is not initialized. Please authenticate first."
)
file_id = file_meta["id"]
file_name = file_meta.get("name", "unknown")
mime_type = file_meta.get("mimeType") or ""
@ -543,6 +578,12 @@ class GoogleDriveConnector(BaseConnector):
- If page_token is None: return all files in one batch.
- Otherwise: return {} and no next_page_token.
"""
# Ensure service is initialized
if self.service is None:
raise RuntimeError(
"Google Drive service is not initialized. Please authenticate first."
)
try:
items = self._iter_selected_items()
@ -560,12 +601,12 @@ class GoogleDriveConnector(BaseConnector):
"next_page_token": None, # no more pages
}
except Exception as e:
# Log the error
try:
logger.error(f"GoogleDriveConnector.list_files failed: {e}")
except Exception:
pass
return {"files": [], "next_page_token": None}
# Log the error and re-raise to surface authentication/permission issues
logger.error(
f"GoogleDriveConnector.list_files failed: {e}",
exc_info=True
)
raise
async def get_file_content(self, file_id: str) -> ConnectorDocument:
"""

View file

@ -60,8 +60,24 @@ class GoogleDriveOAuth:
# If credentials are expired, refresh them
if self.creds and self.creds.expired and self.creds.refresh_token:
self.creds.refresh(Request())
await self.save_credentials()
try:
self.creds.refresh(Request())
await self.save_credentials()
except Exception as e:
# Refresh failed - likely refresh token expired or revoked
# Clear credentials and raise a clear error
self.creds = None
# Try to clean up the invalid token file
if os.path.exists(self.token_file):
try:
os.remove(self.token_file)
except Exception:
pass
raise ValueError(
f"Failed to refresh Google Drive credentials. "
f"The refresh token may have expired or been revoked. "
f"Please re-authenticate: {str(e)}"
) from e
return self.creds

View file

@ -95,6 +95,12 @@ class OneDriveConnector(BaseConnector):
self._default_params = {
"$select": "id,name,size,lastModifiedDateTime,createdDateTime,webUrl,file,folder,@microsoft.graph.downloadUrl"
}
# Selective sync support (similar to Google Drive)
self.cfg = type('OneDriveConfig', (), {
'file_ids': config.get('file_ids') or config.get('selected_files') or config.get('selected_file_ids'),
'folder_ids': config.get('folder_ids') or config.get('selected_folders') or config.get('selected_folder_ids'),
})()
@property
def _graph_base_url(self) -> str:
@ -251,6 +257,10 @@ class OneDriveConnector(BaseConnector):
if not await self.authenticate():
raise RuntimeError("OneDrive authentication failed during file listing")
# If file_ids or folder_ids are specified in config, use selective sync
if self.cfg.file_ids or self.cfg.folder_ids:
return await self._list_selected_files()
files: List[Dict[str, Any]] = []
max_files_value = max_files if max_files is not None else 100
@ -349,6 +359,14 @@ class OneDriveConnector(BaseConnector):
response = await self._make_graph_request(url, params=params)
item = response.json()
# Check if it's a folder
if item.get("folder"):
return {
"id": file_id,
"name": item.get("name", ""),
"isFolder": True,
}
if item.get("file"):
return {
"id": file_id,
@ -360,6 +378,7 @@ class OneDriveConnector(BaseConnector):
"mime_type": item.get("file", {}).get("mimeType", self._get_mime_type(item.get("name", ""))),
"url": item.get("webUrl", ""),
"download_url": item.get("@microsoft.graph.downloadUrl"),
"isFolder": False,
}
return None
@ -429,6 +448,62 @@ class OneDriveConnector(BaseConnector):
response.raise_for_status()
return response
async def _list_selected_files(self) -> Dict[str, Any]:
"""List only selected files/folders (selective sync)."""
files: List[Dict[str, Any]] = []
# Process selected file IDs
if self.cfg.file_ids:
for file_id in self.cfg.file_ids:
try:
file_meta = await self._get_file_metadata_by_id(file_id)
if file_meta and not file_meta.get('isFolder', False):
files.append(file_meta)
elif file_meta and file_meta.get('isFolder', False):
# If it's a folder, expand its contents
folder_files = await self._list_folder_contents(file_id)
files.extend(folder_files)
except Exception as e:
logger.warning(f"Failed to get file {file_id}: {e}")
continue
# Process selected folder IDs
if self.cfg.folder_ids:
for folder_id in self.cfg.folder_ids:
try:
folder_files = await self._list_folder_contents(folder_id)
files.extend(folder_files)
except Exception as e:
logger.warning(f"Failed to list folder {folder_id}: {e}")
continue
return {"files": files, "next_page_token": None}
async def _list_folder_contents(self, folder_id: str) -> List[Dict[str, Any]]:
"""List all files in a folder recursively."""
files: List[Dict[str, Any]] = []
try:
url = f"{self._graph_base_url}/me/drive/items/{folder_id}/children"
params = dict(self._default_params)
response = await self._make_graph_request(url, params=params)
data = response.json()
items = data.get("value", [])
for item in items:
if item.get("file"): # It's a file
file_meta = await self._get_file_metadata_by_id(item.get("id"))
if file_meta:
files.append(file_meta)
elif item.get("folder"): # It's a subfolder, recurse
subfolder_files = await self._list_folder_contents(item.get("id"))
files.extend(subfolder_files)
except Exception as e:
logger.error(f"Failed to list folder contents for {folder_id}: {e}")
return files
def _get_mime_type(self, filename: str) -> str:
"""Get MIME type based on file extension."""
import mimetypes

View file

@ -100,6 +100,12 @@ class SharePointConnector(BaseConnector):
self._default_params = {
"$select": "id,name,size,lastModifiedDateTime,createdDateTime,webUrl,file,folder,@microsoft.graph.downloadUrl"
}
# Selective sync support (similar to Google Drive and OneDrive)
self.cfg = type('SharePointConfig', (), {
'file_ids': config.get('file_ids') or config.get('selected_files') or config.get('selected_file_ids'),
'folder_ids': config.get('folder_ids') or config.get('selected_folders') or config.get('selected_folder_ids'),
})()
@property
def _graph_base_url(self) -> str:
@ -293,6 +299,10 @@ class SharePointConnector(BaseConnector):
if not await self.authenticate():
raise RuntimeError("SharePoint authentication failed during file listing")
# If file_ids or folder_ids are specified in config, use selective sync
if self.cfg.file_ids or self.cfg.folder_ids:
return await self._list_selected_files()
files = []
max_files_value = max_files if max_files is not None else 100
@ -426,6 +436,14 @@ class SharePointConnector(BaseConnector):
"download_url": item.get("@microsoft.graph.downloadUrl")
}
# Check if it's a folder
if item.get("folder"):
return {
"id": file_id,
"name": item.get("name", ""),
"isFolder": True,
}
return None
except Exception as e:
@ -453,6 +471,67 @@ class SharePointConnector(BaseConnector):
logger.error(f"Failed to download file content for {file_id}: {e}")
raise
async def _list_selected_files(self) -> Dict[str, Any]:
"""List only selected files/folders (selective sync)."""
files: List[Dict[str, Any]] = []
# Process selected file IDs
if self.cfg.file_ids:
for file_id in self.cfg.file_ids:
try:
file_meta = await self._get_file_metadata_by_id(file_id)
if file_meta and not file_meta.get('isFolder', False):
files.append(file_meta)
elif file_meta and file_meta.get('isFolder', False):
# If it's a folder, expand its contents
folder_files = await self._list_folder_contents(file_id)
files.extend(folder_files)
except Exception as e:
logger.warning(f"Failed to get file {file_id}: {e}")
continue
# Process selected folder IDs
if self.cfg.folder_ids:
for folder_id in self.cfg.folder_ids:
try:
folder_files = await self._list_folder_contents(folder_id)
files.extend(folder_files)
except Exception as e:
logger.warning(f"Failed to list folder {folder_id}: {e}")
continue
return {"files": files, "next_page_token": None}
async def _list_folder_contents(self, folder_id: str) -> List[Dict[str, Any]]:
"""List all files in a folder recursively."""
files: List[Dict[str, Any]] = []
try:
site_info = self._parse_sharepoint_url()
if site_info:
url = f"{self._graph_base_url}/sites/{site_info['host_name']}:/sites/{site_info['site_name']}:/drive/items/{folder_id}/children"
else:
url = f"{self._graph_base_url}/me/drive/items/{folder_id}/children"
params = dict(self._default_params)
response = await self._make_graph_request(url, params=params)
data = response.json()
items = data.get("value", [])
for item in items:
if item.get("file"): # It's a file
file_meta = await self._get_file_metadata_by_id(item.get("id"))
if file_meta:
files.append(file_meta)
elif item.get("folder"): # It's a subfolder, recurse
subfolder_files = await self._list_folder_contents(item.get("id"))
files.extend(subfolder_files)
except Exception as e:
logger.error(f"Failed to list folder contents for {folder_id}: {e}")
return files
async def _download_file_from_url(self, download_url: str) -> bytes:
"""Download file content from direct download URL"""
try: