跳转至

如何适用 Export API

一般流程

下图为 Export API 的使用流程概览。

图:Export API 工作流

  1. 客户端首先向服务器发送 POST 请求。该请求包括一个 JSON 文件,其中包含所需导出内容的配置设置,并启动服务器上的导出流程。
  2. 作为对 POST 请求的响应,Export API 会返回订单状态(order state,详见下文)。返回的名称(ID)是后续请求所必需的。
  3. 如果 POST 请求成功,客户端会定期向 API 发送 GET 请求,以检查导出的进度。在步骤 2 中收到的名称将作为这些请求的地址的一部分。
  4. 一旦 done 标志被设置为 true,且元数据中的 stateSTATE_SUCCEEDED,客户端即可开始下载导出结果。系统会发送一个最终的 GET 请求,以获取生成的导出文件,这些文件会以 ZIP 压缩包形式提供。如果导出过程失败,且状态为 STATE_FAILED,则无法下载任何文件。此时,响应中将包含相应的错误消息。

响应类型 OrderState

API 的响应包括以下信息:

  • name:出口订单内部管理的唯一 ID。
  • metadata:包含导出过程的当前状态等信息。可能的值有:

    • STATE_UNSPECIFIED:默认状态,通常在调用无效的 GetOparation 时返回
    • STATE_PENDING:出口订单正在排队等待资源。
    • STATE_RUNNING:当前正在执行导出过程。
    • STATE_SUCCEEDED:导出已成功完成。
    • STATE_FAILED:导出过程失败。
  • done:表示导出过程是否已完成的标志。

  • 如果状态为 STATE_FAILED,响应中还将包含详细的错误信息。

有关其他技术细节,请参阅 Swagger UI

授权

要使用 Export API 请求导出,必须先使用用户名和密码在 octoplant 服务器上进行身份验证。

此外,您还需要以下凭证:

  • 客户端 ID:public-api
  • 客户端密钥:ff99971a-f8c3-4d21-5345-f7cb021b4b54

身份验证成功后会返回一个令牌,该令牌必须包含在所有后续的 API 请求中。

Export API 集成示例

以下是 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);
    }
}