← Volver al catálogo
AutomatizacionesReferenciaIntermedioGratis

OneDrive via Microsoft Graph API

Referencia completa para gestionar archivos y carpetas en OneDrive personal y OneDrive for Business mediante la API de Microsoft Graph. Cubre subida, descarga, compartición, búsqueda, sincronización incremental con delta queries y webhooks.

Descargar SKILL.md

Descarga abierta · sin registro · para Microsoft 365, OneDrive, SharePoint

// resultado_de_ejemplo

/**

  • FlowMetrics SaaS — Agente de Sincronización OneDrive
  • =====================================================
  • Gestiona informes mensuales de clientes en OneDrive for Business
  • mediante Microsoft Graph API.
  • Stack: TypeScript · @microsoft/microsoft-graph-client · Azure AD OAuth2
  • Permisos requeridos (Azure AD App):
    • Files.ReadWrite.All
    • Sites.ReadWrite.All (si se usa SharePoint) */

import { Client } from "@microsoft/microsoft-graph-client"; import { TokenCredentialAuthenticationProvider } from "@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials"; import { ClientSecretCredential } from "@azure/identity"; import * as fs from "fs"; import * as path from "path";

// ─── Configuración ────────────────────────────────────────────────────────────

const CONFIG = { tenantId: process.env.AZURE_TENANT_ID!, clientId: process.env.AZURE_CLIENT_ID!, clientSecret: process.env.AZURE_CLIENT_SECRET!, userId: process.env.ONEDRIVE_USER_ID!, // UPN o GUID del account de servicio deltaLinkFile: "./delta_link.json", // Persiste el estado del sync };

// ─── Cliente Graph ─────────────────────────────────────────────────────────────

function createGraphClient(): Client { const credential = new ClientSecretCredential( CONFIG.tenantId, CONFIG.clientId, CONFIG.clientSecret, ); const authProvider = new TokenCredentialAuthenticationProvider(credential, { scopes: ["https://graph.microsoft.com/.default"], }); return Client.initWithMiddleware({ authProvider }); }

const graphClient = createGraphClient(); const userId = CONFIG.userId;

// ─── 1. Información del drive ──────────────────────────────────────────────────

async function getDriveInfo(): Promise { const drive = await graphClient .api(/users/${userId}/drive) .select("id,driveType,quota") .get();

const usedGB = (drive.quota.used / 1e9).toFixed(1); const totalGB = (drive.quota.total / 1e9).toFixed(0); console.log(✓ Drive [${drive.driveType}] ${usedGB} GB / ${totalGB} GB usados); }

// ─── 2. Crear estructura de carpetas ──────────────────────────────────────────

async function ensureFolder(folderPath: string): Promise { /**

  • Crea la carpeta si no existe; devuelve su itemId.
  • Ejemplo: "Clientes/Acme Logistics/2026-06" */ try { const item = await graphClient .api(/users/${userId}/drive/root:/${folderPath}) .select("id,name") .get(); return item.id; // Ya existe } catch { // Construir recursivamente (la API no crea rutas completas de una vez) const parts = folderPath.split("/"); let current = ""; let lastId = "";
for (const part of parts) {
  current = current ? `${current}/${part}` : part;
  try {
    const existing = await graphClient
      .api(`/users/${userId}/drive/root:/${current}`)
      .select("id")
      .get();
    lastId = existing.id;
  } catch {
    const parentPath = current.substring(0, current.lastIndexOf("/")) || "root";
    const parentApi  = parentPath === "root"
      ? `/users/${userId}/drive/root/children`
      : `/users/${userId}/drive/root:/${parentPath}:/children`;

    const created = await graphClient.api(parentApi).post({
      name: part,
      folder: {},
      "@microsoft.graph.conflictBehavior": "rename",
    });
    lastId = created.id;
    console.log(`  + Carpeta creada: ${current}`);
  }
}
return lastId;

} }

// ─── 3. Subir archivos ────────────────────────────────────────────────────────

async function uploadFile( localPath: string, oneDrivePath: string, ): Promise { const stats = fs.statSync(localPath); const fileSize = stats.size; const webUrl: string = fileSize <= 4 * 1024 * 1024 ? await uploadSmall(localPath, oneDrivePath) : await uploadLarge(localPath, oneDrivePath, fileSize); return webUrl; }

async function uploadSmall(localPath: string, oneDrivePath: string): Promise { const buffer = fs.readFileSync(localPath); const uploaded = await graphClient .api(/users/${userId}/drive/root:/${oneDrivePath}:/content) .put(buffer); console.log( ↑ Subido (simple): ${path.basename(localPath)}); return uploaded.webUrl; }

async function uploadLarge( localPath: string, oneDrivePath: string, fileSize: number, ): Promise { const session = await graphClient .api(/users/${userId}/drive/root:/${oneDrivePath}:/createUploadSession) .post({ item: { "@microsoft.graph.conflictBehavior": "replace", name: path.basename(oneDrivePath), }, });

const CHUNK = 10 * 1024 * 1024; // 10 MB (múltiplo de 320 KB) const fd = fs.openSync(localPath, "r"); let offset = 0; let webUrl = "";

while (offset < fileSize) { const length = Math.min(CHUNK, fileSize - offset); const buf = Buffer.alloc(length); fs.readSync(fd, buf, 0, length, offset);

const res = await fetch(session.uploadUrl, {
  method: "PUT",
  headers: {
    "Content-Length": `${length}`,
    "Content-Range":  `bytes ${offset}-${offset + length - 1}/${fileSize}`,
  },
  body: buf,
});

if (res.status === 201 || res.status === 200) {
  const data = (await res.json()) as { webUrl: string };
  webUrl = data.webUrl;
}
offset += length;
console.log(
  `  ↑ [chunked] ${path.basename(localPath)} — ${Math.round((offset / fileSize) * 100)}%`,
);

} fs.closeSync(fd); return webUrl; }

// ─── 4. Generar sharing link con expiración ───────────────────────────────────

async function createShareLink( itemId: string, expiresInDays = 30, ): Promise { const expiry = new Date(); expiry.setDate(expiry.getDate() + expiresInDays);

const link = await graphClient .api(/users/${userId}/drive/items/${itemId}/createLink) .post({ type: "view", scope: "organization", expirationDateTime: expiry.toISOString(), });

console.log( 🔗 Link (expira ${expiresInDays}d): ${link.link.webUrl}); return link.link.webUrl; }

// ─── 5. Invitar a cliente externo ─────────────────────────────────────────────

async function inviteExternal( itemId: string, email: string, message: string, ): Promise { await graphClient .api(/users/${userId}/drive/items/${itemId}/invite) .post({ requireSignIn: true, sendInvitation: true, roles: ["read"], recipients: [{ email }], message, }); console.log( ✉ Invitación enviada a: ${email}); }

// ─── 6. Delta sync incremental ────────────────────────────────────────────────

interface DeltaState { deltaLink: string; lastSync: string; }

async function initialSync(folderPath: string): Promise { let response = await graphClient .api(/users/${userId}/drive/root:/${folderPath}:/delta) .select("id,name,deleted,file,folder,parentReference,lastModifiedDateTime") .get();

const items: unknown[] = [...response.value];

while (response["@odata.nextLink"]) { response = await graphClient.api(response["@odata.nextLink"]).get(); items.push(...response.value); }

const state: DeltaState = { deltaLink: response["@odata.deltaLink"], lastSync: new Date().toISOString(), }; fs.writeFileSync(CONFIG.deltaLinkFile, JSON.stringify(state, null, 2));

console.log(\n📦 Sync inicial — ${items.length} elementos indexados); return state; }

async function incrementalSync(): Promise { if (!fs.existsSync(CONFIG.deltaLinkFile)) { console.warn("No hay deltaLink guardado. Ejecuta initialSync() primero."); return; }

const state: DeltaState = JSON.parse(fs.readFileSync(CONFIG.deltaLinkFile, "utf-8")); const changes = await graphClient.api(state.deltaLink).get();

let added = 0, modified = 0, deleted = 0;

for (const item of changes.value) { if (item.deleted) { console.log( 🗑 Eliminado: ${item.id}); deleted++; } else if (item.file) { console.log( ✎ Archivo ${item.name} — ${new Date(item.lastModifiedDateTime).toLocaleDateString("es-ES")}); modified++; } else if (item.folder) { added++; } }

// Actualizar deltaLink para el próximo sync state.deltaLink = changes["@odata.deltaLink"]; state.lastSync = new Date().toISOString(); fs.writeFileSync(CONFIG.deltaLinkFile, JSON.stringify(state, null, 2));

console.log(\n📊 Cambios desde último sync: +${added} carpetas, ~${modified} archivos, -${deleted} eliminados); }

// ─── 7. Webhook (suscripción a cambios) ───────────────────────────────────────

async function subscribeToChanges(webhookUrl: string): Promise { const expiry = new Date(); expiry.setHours(expiry.getHours() + 4230); // Max para OneDrive: ~43 días

const sub = await graphClient.api("/subscriptions").post({ changeType: "updated,deleted,created", notificationUrl: webhookUrl, resource: /users/${userId}/drive/root, expirationDateTime: expiry.toISOString(), clientState: "flowmetrics-secret-token", });

console.log(\n🔔 Suscripción webhook activa hasta ${new Date(sub.expirationDateTime).toLocaleDateString("es-ES")}); console.log( ID: ${sub.id}); }

// ─── 8. Orquestador principal ─────────────────────────────────────────────────

interface ClientReport { clientName: string; clientEmail: string; period: string; // "2026-06" pdfPath: string; excelPath: string; }

async function syncClientReports(reports: ClientReport[]): Promise { console.log("=".repeat(60)); console.log(" FlowMetrics — Agente Sync OneDrive"); console.log("=".repeat(60));

await getDriveInfo();

for (const report of reports) { const folderPath = Clientes/${report.clientName}/${report.period}; console.log(\n▶ Procesando: ${report.clientName} / ${report.period});

// 2. Asegurar estructura de carpetas
const folderId = await ensureFolder(folderPath);

// 3. Subir archivos
const pdfItem = await graphClient
  .api(`/users/${userId}/drive/root:/${folderPath}/${path.basename(report.pdfPath)}`)
  .select("id,webUrl")
  .get().catch(async () => {
    // Archivo no existe aún, lo subimos
    return graphClient
      .api(`/users/${userId}/drive/root:/${folderPath}/${path.basename(report.pdfPath)}:/content`)
      .put(Buffer.alloc(1024)); // simulado
  });

// 4. Crear link de compartición (30 días)
const shareLink = await createShareLink(pdfItem.id ?? folderId, 30);

// 5. Invitar al cliente con el link
await inviteExternal(
  pdfItem.id ?? folderId,
  report.clientEmail,
  `Hola, adjuntamos el informe de analítica de procesos correspondiente a ${report.period}. ` +
  `Puede acceder desde el siguiente enlace (válido 30 días): ${shareLink}`,
);

}

// 6. Delta sync para detectar cambios posteriores console.log("\n─── Delta Sync ───────────────────────────────────────────"); if (!fs.existsSync(CONFIG.deltaLinkFile)) { await initialSync("Clientes"); } else { await incrementalSync(); }

// 7. Webhook para notificaciones en tiempo real await subscribeToChanges("https://api.flowmetrics.io/webhooks/onedrive");

console.log("\n✅ Sincronización completada."); }

// ─── Ejecución ────────────────────────────────────────────────────────────────

const REPORTS: ClientReport[] = [ { clientName: "Acme Logistics", clientEmail: "ops@acmelogistics.com", period: "2026-06", pdfPath: "./reports/acme-logistics-2026-06.pdf", excelPath: "./reports/acme-logistics-2026-06-data.xlsx", }, { clientName: "TechGroup EU", clientEmail: "analytics@techgroupeu.com", period: "2026-06", pdfPath: "./reports/techgroupeu-2026-06.pdf", excelPath: "./reports/techgroupeu-2026-06-data.xlsx", }, { clientName: "Retail Prime ES", clientEmail: "datateam@retailprime.es", period: "2026-06", pdfPath: "./reports/retailprime-2026-06.pdf", excelPath: "./reports/retailprime-2026-06-data.xlsx", }, ];

syncClientReports(REPORTS).catch(console.error);

// qué_hace

Permite a agentes IA gestionar archivos en OneDrive (subir, descargar, compartir, sincronizar) mediante la API de Microsoft Graph.

// cómo_lo_hace

Proporciona fragmentos TypeScript listos para usar con el cliente Graph: operaciones CRUD de archivos, sesiones de subida resumible para archivos grandes, delta queries para sync incremental y creación de enlaces de compartición con expiración.

// ejemplo_de_uso

Úsala cuando un agente IA necesita guardar o recuperar archivos del OneDrive corporativo como parte de un flujo de trabajo. Ej.: el agente sube el informe PDF generado automáticamente a una carpeta compartida y crea un enlace con expiración de 7 días.

// plataformas

Microsoft 365OneDriveSharePointAzure AD
Categoría
Automatizaciones
Tipo
Referencia
Nivel
Intermedio
Licencia
Apache-2.0
Seguridad
seguro · riesgo bajo
Versión
1.0.0

// opiniones_de_la_comunidad

Opiniones

Cargando opiniones…

// pase_cultiva_ia

Llévate todo el arsenal con el Pase

Todas las skills, prompts y automatizaciones del catálogo en un único archivo, listas para usar: un pago, acceso de por vida y las novedades que añadamos. Sin suscripción.

Pago único · IVA incluido · pago seguro con Stripe.

Acceso inmediato · si no es lo que esperabas, te devolvemos los 10 €.