Zum Inhalt

So verwenden Sie die Export-API

Allgemeines Vorgehen

Das folgende Diagramm bietet einen visuellen Überblick über die Vorgehensweise der Verwendung der Export API.

Abbildung: Arbeitsablauf der Export API

  1. Der Client sendet einen POST Request an den Server. Dieser Request enthält eine JSON-Datei mit den Konfigurationseinstellungen für die gewünschten Exporte und leitet den Exportprozess auf dem Server ein.
  2. Als Antwort auf den POST Request gibt die Export API einen Auftragsstatus zurück (weiter unten genauer beschrieben). Der zurückgegebene Name (ID) ist für nachfolgende Anfragen erforderlich.
  3. Wenn der POST Request erfolgreich ist, sendet der Client regelmäßig GET Requests an die API, um den Fortschritt des Exports zu überprüfen. Der in Schritt 2 erhaltene Name wird als Teil der Adresse für diese Requests verwendet.
  4. Sobald das done flag auf true und der state in den Metadaten auf STATE_SUCCEEDED gesetzt ist, kann der Client mit dem Download des Exports fortfahren. Ein letzter GET Request wird gesendet, um die generierten Exportdateien abzurufen, die in einem komprimierten ZIP-Archiv bereitgestellt werden. Wenn der Export fehlschlägt und der state STATE_FAILED lautet, ist kein Download möglich. In diesem Fall enthält die Response eine entsprechende Fehlermeldung.

Response-Typ OrderState

Die Response der API enthält folgende Informationen:

  • name: Eine eindeutige ID, unter welcher der Exportauftrag intern verwaltet wird.
  • Metadaten: Enthält u. a. den aktuellen Stand des Exportvorgangs. Mögliche Werte sind:

    • STATE_UNSPECIFIED: Standardzustand, wird normalerweise bei ungültigen GetOparation Aufrufen zurückgegeben
    • STATE_PENDING: Der Exportauftrag steht in der Warteschlange und wartet auf eine Ressource.
    • STATE_RUNNING: Der Exportvorgang wird gerade durchgeführt.
    • STATE_SUCCEEDED: Der Export wurde erfolgreich abgeschlossen.
    • STATE_FAILED: Der Exportvorgang ist fehlgeschlagen.
  • erledigt: Ein Flag, das anzeigt, ob der Export abgeschlossen ist.

  • Wenn der Status STATE_FAILED ist, werden in der Antwort auch detaillierte Fehlerinformationen angegeben.

Weitere technische Details finden Sie in der Swagger UI.

Autorisierung

Um einen Export über die Export API anzufordern, müssen Sie sich zunächst mit Ihrem Benutzernamen und Passwort am octoplant Server anmelden.

Außerdem benötigen Sie die folgenden Zugangsdaten:

  • Client-ID: public-api
  • Client Secret: ff99971a-f8c3-4d21-5345-f7cb021b4b54

Bei erfolgreicher Authentifizierung wird ein Token zurückgegeben, das bei allen nachfolgenden API-Requests angegeben werden muss.

Beispiel für die Integration der Export API

Nachfolgend finden Sie ein Beispiel für die Integration der Export API:

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);
    }
}