RecordTools.js•9.95 kB
// src/server/tools/RecordTools.js
import { KintoneRecord } from '../../models/KintoneRecord.js';
import { ValidationUtils } from '../../utils/ValidationUtils.js';
import { LoggingUtils } from '../../utils/LoggingUtils.js';
import { ResponseBuilder } from '../../utils/ResponseBuilder.js';
// レコード関連のツールを処理する関数
export async function handleRecordTools(name, args, repository) {
// 共通のツール実行ログ
LoggingUtils.logToolExecution('record', name, args);
switch (name) {
case 'get_record': {
ValidationUtils.validateRequired(args, ['app_id', 'record_id']);
const record = await repository.getRecord(args.app_id, args.record_id);
return record.fields; // KintoneRecord ではなく fields を返す
}
case 'search_records': {
ValidationUtils.validateRequired(args, ['app_id']);
// クエリーが提供されている場合、kintoneクエリー構文を検証
if (args.query) {
ValidationUtils.validateKintoneQuery(args.query);
}
const records = await repository.searchRecords(
args.app_id,
args.query,
args.fields
);
return records.map(r => r.fields);
}
case 'create_record': {
ValidationUtils.validateRequired(args, ['app_id', 'fields']);
ValidationUtils.validateObject(args.fields, 'fields');
const recordId = await repository.createRecord(
args.app_id,
args.fields
);
return ResponseBuilder.recordCreated(recordId);
}
case 'update_record': {
ValidationUtils.validateRequired(args, ['app_id', 'fields']);
// レコードIDまたはupdateKeyのいずれかが必要
if (!args.record_id && !args.updateKey) {
throw new Error('record_id または updateKey は必須パラメータです。');
}
ValidationUtils.validateObject(args.fields, 'fields');
let response;
if (args.record_id) {
// レコードIDを使用した更新
response = await repository.updateRecord(
new KintoneRecord(
args.app_id,
args.record_id,
args.fields
)
);
} else {
// updateKeyを使用した更新
response = await repository.updateRecordByKey(
args.app_id,
args.updateKey.field,
args.updateKey.value,
args.fields
);
}
return ResponseBuilder.recordUpdated(response.revision);
}
case 'add_record_comment': {
ValidationUtils.validateRequired(args, ['app_id', 'record_id', 'text']);
ValidationUtils.validateString(args.text, 'text');
const commentId = await repository.addRecordComment(
args.app_id,
args.record_id,
args.text,
args.mentions || []
);
return ResponseBuilder.withId('comment_id', commentId);
}
case 'update_record_status': {
ValidationUtils.validateRequired(args, ['app_id', 'record_id', 'action']);
ValidationUtils.validateString(args.action, 'action');
const response = await repository.updateRecordStatus(
args.app_id,
args.record_id,
args.action,
args.assignee
);
return ResponseBuilder.withRevision(response.revision);
}
case 'update_record_assignees': {
ValidationUtils.validateRequired(args, ['app_id', 'record_id', 'assignees']);
ValidationUtils.validateArray(args.assignees, 'assignees', {
maxLength: 100
});
const response = await repository.updateRecordAssignees(
args.app_id,
args.record_id,
args.assignees
);
return ResponseBuilder.withRevision(response.revision);
}
case 'get_record_comments': {
ValidationUtils.validateRequired(args, ['app_id', 'record_id']);
const PAGE_SIZE = 10;
const order = (args.order ?? 'desc').toString();
ValidationUtils.validateString(order, 'order', { allowedValues: ['asc', 'desc'] });
const offset = args.offset ?? 0;
ValidationUtils.validateNumber(offset, 'offset', { min: 0, allowString: true });
const limitProvided = args.limit !== undefined && args.limit !== null && args.limit !== '';
let targetLimit = null;
if (limitProvided) {
targetLimit = args.limit;
ValidationUtils.validateNumber(targetLimit, 'limit', { min: 1, allowString: true });
targetLimit = Number(targetLimit);
}
const apiOrder = targetLimit === null ? 'asc' : order;
const outputOrder = order;
let currentOffset = Number(offset);
const collected = [];
let totalCount = null;
while (true) {
const response = await repository.getRecordComments(
args.app_id,
args.record_id,
apiOrder,
currentOffset,
PAGE_SIZE
);
const fetched = response.comments || [];
totalCount = response.totalCount ?? totalCount;
collected.push(...fetched);
const hasMore = apiOrder === 'asc'
? Boolean(response?.newer)
: Boolean(response?.older);
const reachedLimit = targetLimit !== null && collected.length >= targetLimit;
if (fetched.length === 0 || reachedLimit || !hasMore) {
break;
}
currentOffset += PAGE_SIZE;
}
const sorted = [...collected].sort((a, b) => {
const aId = Number(a.id);
const bId = Number(b.id);
return outputOrder === 'asc' ? aId - bId : bId - aId;
});
const finalComments = targetLimit !== null ? sorted.slice(0, targetLimit) : sorted;
const finalTotalCount = totalCount ?? (targetLimit !== null ? finalComments.length : sorted.length);
return {
comments: finalComments,
totalCount: finalTotalCount,
order: outputOrder
};
}
case 'create_records': {
ValidationUtils.validateRequired(args, ['app_id', 'records']);
ValidationUtils.validateArray(args.records, 'records', {
minLength: 1,
maxLength: 100
});
const result = await repository.createRecords(args.app_id, args.records);
return ResponseBuilder.recordsCreated(result.ids, result.revisions);
}
case 'upsert_record': {
ValidationUtils.validateRequired(args, ['app_id', 'updateKey', 'fields']);
ValidationUtils.validateObject(args.updateKey, 'updateKey');
ValidationUtils.validateString(args.updateKey.field, 'updateKey.field');
ValidationUtils.validateObject(args.fields, 'fields');
const result = await repository.upsertRecord(
args.app_id,
args.updateKey,
args.fields
);
return {
record_id: result.id,
revision: result.revision,
message: `レコードをupsertしました (ID: ${result.id})`,
operation: result.operation
};
}
case 'upsert_records': {
ValidationUtils.validateRequired(args, ['app_id', 'records']);
ValidationUtils.validateArray(args.records, 'records', {
minLength: 1,
maxLength: 100
});
const normalizedRecords = args.records.map((entry, index) => {
ValidationUtils.validateObject(entry, `records[${index}]`);
ValidationUtils.validateObject(entry.updateKey, `records[${index}].updateKey`);
ValidationUtils.validateString(entry.updateKey.field, `records[${index}].updateKey.field`);
if (entry.updateKey.value === undefined || entry.updateKey.value === null) {
throw new Error(`records[${index}].updateKey.value は必須です。`);
}
ValidationUtils.validateObject(entry.fields, `records[${index}].fields`);
return {
updateKey: {
field: entry.updateKey.field,
value: entry.updateKey.value
},
fields: entry.fields
};
});
const result = await repository.upsertRecords(args.app_id, normalizedRecords);
return {
records: result.map((r) => ({
record_id: r.id,
revision: r.revision,
operation: r.operation
})),
message: `${result.length}件のレコードをupsertしました`
};
}
default:
throw new Error(`Unknown record tool: ${name}`);
}
}