143 lines
4.9 KiB
JavaScript
143 lines
4.9 KiB
JavaScript
/**
|
||
* get-master-data.js
|
||
* ------------------------------------------------------------
|
||
* Pleasanterのユーザー/組織(部署)/グループ一覧を取得し、
|
||
* ./configs/ 配下に保存します(resolve-names.js が読み込む)。
|
||
*
|
||
* 使い方:
|
||
* node get-master-data.js
|
||
* node get-master-data.js ./config.json ← 設定ファイルを明示指定する場合
|
||
*
|
||
* 参照した公式マニュアル:
|
||
* - 開発者向け機能:API:ユーザ操作:ユーザ取得(全て)
|
||
* https://pleasanter.org/ja/manual/api-user-get-all
|
||
* - 開発者向け機能:API:組織操作:組織取得
|
||
* https://pleasanter.org/ja/manual/api-dept-get
|
||
* - 開発者向け機能:API:グループ操作:グループ取得
|
||
* https://pleasanter.org/ja/manual/api-group-get
|
||
* ------------------------------------------------------------
|
||
*/
|
||
|
||
const fs = require("fs");
|
||
const path = require("path");
|
||
|
||
const TARGETS = [
|
||
{ key: "users", endpoint: "users/get", outFile: "master_users.json", label: "ユーザー", idField: "UserId" },
|
||
{ key: "depts", endpoint: "depts/get", outFile: "master_depts.json", label: "組織", idField: "DeptId" },
|
||
{ key: "groups", endpoint: "groups/get", outFile: "master_groups.json", label: "グループ", idField: "GroupId" },
|
||
];
|
||
|
||
// 一部API(/api/users/get 等)はページング応答(TotalCount > 1回あたりのData件数)を返すため、
|
||
// 全件取得できるまで Offset をずらして繰り返し取得し、Data配列を結合する。
|
||
async function fetchAndSave(target, { BaseUrl, ApiKey, ApiVersion }, outDir) {
|
||
const url = `${BaseUrl.replace(/\/+$/, "")}/api/${target.endpoint}`;
|
||
|
||
let allData = [];
|
||
let offset = 0;
|
||
let totalCount = null;
|
||
let firstJson = null;
|
||
|
||
while (true) {
|
||
const body = {
|
||
ApiVersion: ApiVersion || "1.1",
|
||
ApiKey: ApiKey,
|
||
Offset: offset,
|
||
};
|
||
|
||
console.log(`[INFO] 取得先: ${url} (Offset: ${offset})`);
|
||
|
||
let response;
|
||
try {
|
||
response = await fetch(url, {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify(body),
|
||
});
|
||
} catch (err) {
|
||
console.error(`[エラー] ${target.label}の取得に失敗しました:`, err.message);
|
||
return false;
|
||
}
|
||
|
||
const text = await response.text();
|
||
|
||
if (!response.ok) {
|
||
console.error(`[エラー] HTTP ${response.status} ${response.statusText}`);
|
||
console.error(text);
|
||
return false;
|
||
}
|
||
|
||
let json;
|
||
try {
|
||
json = JSON.parse(text);
|
||
} catch (err) {
|
||
console.error(`[エラー] ${target.label}のレスポンスがJSONとして解析できませんでした。`);
|
||
console.error(text);
|
||
return false;
|
||
}
|
||
|
||
if (!firstJson) firstJson = json;
|
||
const pageData = json?.Response?.Data || [];
|
||
allData = allData.concat(pageData);
|
||
totalCount = json?.Response?.TotalCount ?? allData.length;
|
||
|
||
if (pageData.length === 0 || allData.length >= totalCount) break;
|
||
offset = allData.length;
|
||
}
|
||
|
||
// ページ間でOffset起点がずれて重複が混ざる場合があるため、idFieldで重複排除する
|
||
const seen = new Set();
|
||
allData = allData.filter((item) => {
|
||
const id = item[target.idField];
|
||
if (seen.has(id)) return false;
|
||
seen.add(id);
|
||
return true;
|
||
});
|
||
|
||
// 結合済みのData配列で1件目のレスポンスを上書きし、全件入りのJSONとして保存する
|
||
const outJson = { ...firstJson, Response: { ...firstJson.Response, Data: allData } };
|
||
const outFile = path.join(outDir, target.outFile);
|
||
fs.writeFileSync(outFile, JSON.stringify(outJson, null, 2), "utf-8");
|
||
|
||
console.log(`[OK] ${target.label}: ${allData.length}/${totalCount}件 保存しました: ${outFile}`);
|
||
|
||
return true;
|
||
}
|
||
|
||
async function main() {
|
||
const configPath = process.argv[2] || path.join(path.join(__dirname, "..", ".."), "config.json");
|
||
|
||
if (!fs.existsSync(configPath)) {
|
||
console.error(`[エラー] 設定ファイルが見つかりません: ${configPath}`);
|
||
console.error("config.example.json をコピーして config.json を作成してください。");
|
||
process.exit(1);
|
||
}
|
||
|
||
const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
||
const { BaseUrl, ApiKey, ApiVersion } = config;
|
||
|
||
if (!BaseUrl || !ApiKey) {
|
||
console.error("[エラー] config.json に BaseUrl / ApiKey を設定してください。");
|
||
process.exit(1);
|
||
}
|
||
|
||
const outDir = path.join(path.join(__dirname, "..", ".."), "configs", "master");
|
||
if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
|
||
|
||
const results = [];
|
||
for (const target of TARGETS) {
|
||
const ok = await fetchAndSave(target, { BaseUrl, ApiKey, ApiVersion }, outDir);
|
||
results.push({ target: target.label, ok });
|
||
}
|
||
|
||
console.log("\n==== 取得結果まとめ ====");
|
||
for (const { target, ok } of results) {
|
||
console.log(` ${target}: ${ok ? "OK" : "失敗"}`);
|
||
}
|
||
|
||
if (results.some((r) => !r.ok)) {
|
||
process.exit(1);
|
||
}
|
||
}
|
||
|
||
main();
|