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:
commit
c7a8e89132
9 changed files with 325 additions and 89 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue