Saltar a contenido

Usar la API de exportación

Proceso general

El diagrama siguiente ofrece un resumen visual del proceso de uso de la API de exportación.

Imagen: Proceso de exportación de la API

  1. El cliente comienza enviando una solicitud POST al servidor. Esta solicitud incluye un archivo JSON que contiene las configuraciones para las exportaciones requeridas e inicia el proceso de exportación en el servidor.
  2. En respuesta a la solicitud POST, la API de exportación devuelve un estado de pedido (descrito con más detalle a continuación). El nombre devuelto (ID) es necesario para las solicitudes posteriores.
  3. Si la solicitud POST tiene éxito, el cliente envía periódicamente solicitudes GET a la API para comprobar el progreso de la exportación. El nombre recibido en el paso 2 se utiliza como parte de la dirección para estas solicitudes.
  4. Una vez que el indicador done se establece en true y el estado en los metadatos es STATE_SUCCEED, el cliente puede proceder a la descarga de la exportación. Se envía una última solicitud GET para recuperar los archivos de exportación generados, que se proporcionan en un archivo ZIP comprimido. Si el proceso de exportación falla y el estado es STATE_FAILED, no habrá ninguna descarga disponible. En este caso, la respuesta incluirá un mensaje de error apropiado.

Tipo de respuesta OrderState

La respuesta de la API incluye la siguiente información:

  • nombre: ID único con el que se gestiona internamente el proceso de exportación.
  • metadatos: Contiene el estado actual del proceso de exportación, entre otra información. Los valores posibles son:

    • STATE_UNSPECIFIED: Estado estándar, normalmente devuelto para llamadas GetOparation no válidas.
    • STATE_PENDING: El pedido de exportación está pendiente y a la espera de recursos.
    • STATE_RUNNING: Proceso de exportación en curso.
    • STATE_SUCCEEDED: El proceso de exportación se completó con éxito.
    • STATE_FAILED: El proceso de exportación falló.
  • finalizado: Bandera que indica si el proceso de exportación finalizó.

  • Si el estado es STATE_FAILED, también se incluirá información detallada del error en la respuesta.

Para más detalles técnicos, consulte Swagger UI.

Autorización

Para solicitar una exportación utilizando la API de exportación, primero debe autenticarse en el servidor de octoplant utilizando su nombre de usuario y contraseña.

Además, necesitará las siguientes credenciales:

  • ID de cliente: public-api
  • Secreto del cliente: ff99971a-f8c3-4d21-5345-f7cb021b4b54

Una autenticación correcta devuelve un token, que debe incluirse en todas las solicitudes API posteriores.

Ejemplo de integración de la API de exportación

A continuación se muestra un ejemplo de integración de la API de exportación:

const clientId = "public-api";
const clientSecret = "ff99971a-f8c3-4d21-5345-f7cb021b4b54";
const username = "versiondog";              // octoplant user username
const password = "change-it";               // octoplant user password
const baseUrl = "https://localhost:64023";  // octoplant server address and port of the api

function grpcStatusCodeToText(code) {
    switch (code) {
        case 0: return "OK";
        case 1: return "CANCELLED";
        case 2: return "UNKNOWN";
        case 3: return "INVALID_ARGUMENT";
        case 4: return "DEADLINE_EXCEEDED";
        case 5: return "NOT_FOUND";
        case 6: return "ALREADY_EXISTS";
        case 7: return "PERMISSION_DENIED";
        case 8: return "RESOURCE_EXHAUSTED";
        case 9: return "FAILED_PRECONDITION";
        case 10: return "ABORTED";
        case 11: return "OUT_OF_RANGE";
        case 12: return "UNIMPLEMENTED";
        case 13: return "INTERNAL";
        case 14: return "UNAVAILABLE";
        case 15: return "DATA_LOSS";
        case 16: return "UNAUTHENTICATED";
        default: return `Unknown code ${code}`;
    }
}

async function handleErrorResponse(response) {
    if (response.ok)
        return
    let error = await response.json();
    if (error.code)
        error["codeText"] = grpcStatusCodeToText(error.code);
    if (error.error.code)
        error.error["codeText"] = grpcStatusCodeToText(error.error.code);
    console.dir(error);
    throw new Error(`Response status: ${response.status}`);
}

// send the post / get request message
async function fetchGrpc(input, init) {
    if (init.body)
        console.log(`${init.method}: ${input} with body: ${init.body}`);
    else
        console.log(`${init.method}: ${input}`);

    const response = await fetch(input, init);
    await handleErrorResponse(response);
    return response;
}

// authentication returns a token for the requests
async function authenticate() {
    const response = await fetchGrpc(`${baseUrl}/v1/oauth2/token`, {
        "credentials": "include",
        "headers": {
            "Content-Type": "application/x-www-form-urlencoded"
        },
        "body": `grant_type=password&username=${username}&password=${password}&client_id=${clientId}&client_secret=${clientSecret}`,
        "method": "POST",
        "mode": "cors"
    });
    const authData = await response.json()
    return authData.access_token
}

// request the export order
async function startExport(token, data) {
    const response = await fetchGrpc(`${baseUrl}/v1/order`, {
        "credentials": "include",
        "headers": {
            "Accept": "application/json",
            "authorization": `Bearer ${token}`
        },
        "body": JSON.stringify(data),
        "method": "POST",
        "mode": "cors"
    });
    return await response.json()
}

// request a cancel of the ordered export
async function cancelExport(token, name) {
    const response = await fetchGrpc(`${baseUrl}/v1/order/${name}/cancel`, {
        "credentials": "include",
        "headers": {
            "Accept": "application/json",
            "authorization": `Bearer ${token}`
        },
        "method": "POST",
        "mode": "cors"
    });
    return await response.json()
}

// request and perform the download of the ordered export 
async function downloadExport(token, name) {
    const response = await fetchGrpc(`${baseUrl}/v1/order/${name}/download`, {
        "credentials": "include",
        "headers": {
            "Accept": "application/json",
            "authorization": `Bearer ${token}`
        },
        "method": "GET",
        "mode": "cors"
    });
    return await response.blob()
}

// request the status of the export
async function getExport(token, name) {
    const response = await fetchGrpc(`${baseUrl}/v1/order/${name}`, {
        "credentials": "include",
        "headers": {
            "Accept": "application/json",
            "authorization": `Bearer ${token}`
        },
        "method": "GET",
        "mode": "cors"
    });
    let j = await response.json()
    if (j.error && j.error.code) {
        j.error.codeText = grpcStatusCodeToText(j.error.code);
    }
    return j;
}

async function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

// create a simple export
async function simpleExport(token) {

    // request export with the json for the wished exports - here: project tree
    let e = await startExport(token, {
        exportContents: {
            projectTree: {}
        }
    });
    console.dir(e);

    // request the status of the export
    // if done is set to true: the creation is finished (succesful or failed)
    do {
        await sleep(1000);
        e = await getExport(token, e.name);
        // print the response (JSON) - just for documentation
        console.dir(e);
    } while (!e.done)

    // download the ordered export.
    // the zip package includes a contents.json and all requested exports
    let d = await downloadExport(token, e.name)
    // Just printing the blob - actually save the zip file
    console.dir(d)
}

async function main() {
    const token = await authenticate();

    const fn = simpleExport;
    try {
        console.log(`Running function: ${fn.name}`);
        await fn(token);
    } catch (e) {
        console.error(`Error in function ${fn.name}:`, e);
    }
}