如何适用 Export API
一般流程
下图为 Export API 的使用流程概览。
- 客户端首先向服务器发送 POST 请求。该请求包括一个 JSON 文件,其中包含所需导出内容的配置设置,并启动服务器上的导出流程。
- 作为对 POST 请求的响应,Export API 会返回订单状态(order state,详见下文)。返回的名称(ID)是后续请求所必需的。
- 如果 POST 请求成功,客户端会定期向 API 发送 GET 请求,以检查导出的进度。在步骤 2 中收到的名称将作为这些请求的地址的一部分。
- 一旦 done 标志被设置为 true,且元数据中的 state 为 STATE_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);
}
}