シェルの場合(こっちはいちおう動くこと確認できた。Kibana 8.18.8で動くこと確認済み)
#!/bin/bash
set -e
TOKEN="MWMxMmhab0JWNUp6TGlPTUtFbHc6LW13NHpJbmhzenN3WWJqVzdBVVBMdw=="
KIBANA_URL="http://localhost:5601"
DASHBOARD_ID="dfd3b3b4-41bc-44f3-a459-bad56dab9b59"
VIS_IDS_FILE="$1"
if [ -z "$KIBANA_URL" ] || [ -z "$DASHBOARD_ID" ] || [ -z "$VIS_IDS_FILE" ]; then
echo "Usage: $0 <kibana_url> <dashboard_id> <vis_ids.json>"
exit 1
fi
echo "Loading VIS_IDS from $VIS_IDS_FILE ..."
VIS_IDS=$(jq -r '.[]' "$VIS_IDS_FILE")
echo "VISUALIZATION IDs:"
echo "$VIS_IDS"
echo "----------------------------------------"
# ================
# STEP 1: Dashboard を取得
# ================
echo "Fetching dashboard: $DASHBOARD_ID ..."
DASH=$(curl -s -X GET "$KIBANA_URL/api/saved_objects/dashboard/$DASHBOARD_ID" \
-H "kbn-xsrf: true")
PANELS=$(echo "$DASH" | jq -r '.attributes.panelsJSON')
if [ "$PANELS" = "null" ] || [ -z "$PANELS" ]; then
PANELS="[]"
fi
NEW_PANELS=$(echo "$PANELS" | jq '.')
# 自動レイアウト(2カラム)
X=0
Y=0
# 既存 index 数
CURRENT_COUNT=$(echo "$NEW_PANELS" | jq length)
NEXT_INDEX=$((CURRENT_COUNT + 1))
# ================
# STEP 2: panelJSON を追加
# ================
for VIS_ID in $VIS_IDS; do
PANEL=$(cat <<EOF
{
"panelIndex": "$NEXT_INDEX",
"type": "visualization",
"id": "$VIS_ID",
"gridData": {
"x": $X,
"y": $Y,
"w": 48,
"h": 15,
"i": "$NEXT_INDEX"
},
"version": "1"
}
EOF
)
NEW_PANELS=$(echo "$NEW_PANELS" | jq ". + [ $PANEL ]")
# 次のパネル配置(2カラム)
if [ $X -eq 0 ]; then
X=24
else
X=0
Y=$((Y + 15))
fi
NEXT_INDEX=$((NEXT_INDEX + 1))
done
# ================
# STEP 3: Dashboard を更新
# ================
ATTR_ONLY=$(echo "$DASH" | jq '.attributes')
UPDATED_ATTR=$(echo "$ATTR_ONLY" | jq --arg panels "$(echo "$NEW_PANELS" | jq -c '.')" \
'.panelsJSON = $panels')
echo "Updating dashboard ..."
curl -v -X PUT "$KIBANA_URL/api/saved_objects/dashboard/$DASHBOARD_ID" \
-H "kbn-xsrf: true" \
-H "Authorization: ApiKey ${TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"attributes\": $UPDATED_ATTR }"
echo "Done! Panels added to dashboard."Javaの場合(こっちは動くか確認できていない)
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.*;
import java.io.File;
import java.io.IOException;
import java.util.*;
public class AddPanelsToDashboard {
private static final ObjectMapper mapper = new ObjectMapper();
private static final OkHttpClient client = new OkHttpClient();
public static void main(String[] args) throws Exception {
if (args.length < 3) {
System.out.println("Usage: java AddPanelsToDashboard <kibana_url> <dashboard_id> <vis_ids.json> [api_key]");
return;
}
String kibanaUrl = args[0];
String dashboardId = args[1];
String visJsonPath = args[2];
String apiKey = args.length >= 4 ? args[3] : null;
// VIS IDS 読み込み
List<String> visIds = mapper.readValue(new File(visJsonPath), new TypeReference<List<String>>() {});
System.out.println("Loaded VIS IDS: " + visIds);
// Dashboard 取得
String dashApi = kibanaUrl + "/api/saved_objects/dashboard/" + dashboardId;
String dashJson = httpGet(dashApi, apiKey);
Map<String, Object> dashMap = mapper.readValue(dashJson, new TypeReference<Map<String, Object>>() {});
Map<String, Object> attributes = (Map<String, Object>) dashMap.get("attributes");
// panelsJSON を取得
List<Object> panels = new ArrayList<>();
if (attributes.get("panelsJSON") != null && !attributes.get("panelsJSON").toString().isEmpty()) {
panels = mapper.readValue(attributes.get("panelsJSON").toString(), new TypeReference<List<Object>>() {});
}
int nextIndex = panels.size() + 1;
int x = 0;
int y = 0;
// VIS_ID ごとに panel を追加(横幅 48 → 全幅)
for (String id : visIds) {
Map<String, Object> panel = new LinkedHashMap<>();
panel.put("panelIndex", String.valueOf(nextIndex));
panel.put("type", "visualization");
panel.put("id", id);
Map<String, Object> grid = new LinkedHashMap<>();
grid.put("x", 0);
grid.put("y", y);
grid.put("w", 48); // 全幅
grid.put("h", 15);
grid.put("i", String.valueOf(nextIndex));
panel.put("gridData", grid);
panel.put("version", "1");
panels.add(panel);
y += 15;
nextIndex++;
}
// 新 panelsJSON をセット
attributes.put("panelsJSON", mapper.writeValueAsString(panels));
// Dashboard 更新
Map<String, Object> updateBody = new LinkedHashMap<>();
updateBody.put("attributes", attributes);
String updateJson = mapper.writeValueAsString(updateBody);
String putApi = kibanaUrl + "/api/saved_objects/dashboard/" + dashboardId;
String result = httpPut(putApi, updateJson, apiKey);
System.out.println("Update Result: " + result);
System.out.println("Done!");
}
// =======================
// HTTP GET
// =======================
private static String httpGet(String url, String apiKey) throws IOException {
Request.Builder builder = new Request.Builder()
.url(url)
.addHeader("kbn-xsrf", "true");
if (apiKey != null) {
builder.addHeader("Authorization", "ApiKey " + apiKey);
}
Request request = builder.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
// =======================
// HTTP PUT
// =======================
private static String httpPut(String url, String json, String apiKey) throws IOException {
RequestBody body = RequestBody.create(json, MediaType.parse("application/json"));
Request.Builder builder = new Request.Builder()
.url(url)
.put(body)
.addHeader("kbn-xsrf", "true");
if (apiKey != null) {
builder.addHeader("Authorization", "ApiKey " + apiKey);
}
Request request = builder.build();
Response response = client.newCall(request).execute();
return response.body().string();
}
}