// src/repositories/KintoneFormRepository.js
import { BaseKintoneRepository } from './base/BaseKintoneRepository.js';
import { KintoneApiError } from './base/http/KintoneApiError.js';
import { validateFieldCode, validateOptions, validateField } from './validators/FieldValidator.js';
import { validateFormLayout, validateFieldSize } from './validators/LayoutValidator.js';
import { autoCorrectOptions } from './validators/OptionValidator.js';
import { FIELD_TYPES_REQUIRING_OPTIONS, SUBTABLE_FIELD_TYPE } from '../constants.js';
import { autoCorrectLayoutWidths, validateFieldsInLayout, addMissingFieldsToLayout } from '../utils/LayoutUtils.js';
import { LoggingUtils } from '../utils/LoggingUtils.js';
function logLookupFieldSummary(appId, properties, source) {
if (!properties) {
return;
}
const lookupCodes = Object.entries(properties)
.filter(([, field]) => field && field.lookup !== undefined)
.map(([code]) => code);
if (lookupCodes.length > 0) {
LoggingUtils.debug('form', 'lookup_fields_detected', {
appId,
source,
count: lookupCodes.length,
codes: lookupCodes
});
}
}
/**
* kintoneã¢ããªã®ãã©ãŒã é¢é£æäœãæ
åœãããªããžããªã¯ã©ã¹
*/
export class KintoneFormRepository extends BaseKintoneRepository {
/**
* ã¢ããªã®ãã£ãŒã«ãæ
å ±ãååŸ
* @param {number} appId ã¢ããªID
* @returns {Promise<Object>} ãã£ãŒã«ãæ
å ±
*/
async getFormFields(appId) {
try {
LoggingUtils.info('form', 'get_form_fields', { appId });
try {
const response = await this.client.app.getFormFields({ app: appId });
logLookupFieldSummary(appId, response.properties, 'production');
return response;
} catch (error) {
if (error instanceof KintoneApiError &&
(error.code === 'GAIA_AP01' || error.status === 404)) {
throw new Error(
'察象ã¢ããªã®ãã£ãŒã«ãæ
å ±ãæ¬çªç°å¢ã§èŠã€ãããŸããã§ããããã¬ãã¥ãŒç°å¢ã®æ
å ±ãå¿
èŠãªå Žå㯠get_preview_form_fields ããŒã«ã䜿çšããŠãã ããã'
);
}
throw error;
}
} catch (error) {
this.handleKintoneError(error, `get form fields for app ${appId}`);
}
}
/**
* ã¢ããªã®ãã©ãŒã ã¬ã€ã¢ãŠãæ
å ±ãååŸ
* @param {number} appId ã¢ããªID
* @returns {Promise<Object>} ã¬ã€ã¢ãŠãæ
å ±
*/
async getFormLayout(appId) {
try {
LoggingUtils.info('form', 'get_form_layout', { appId });
try {
const response = await this.client.app.getFormLayout({ app: appId });
LoggingUtils.debug('form', 'get_form_layout_response', response);
return response;
} catch (error) {
// 404ãšã©ãŒïŒã¢ããªãèŠã€ãããªãïŒã®å Žåããã¬ãã¥ãŒç°å¢ã®APIã詊ã
if (error instanceof KintoneApiError &&
(error.code === 'GAIA_AP01' || error.status === 404)) {
// ãã¬ãã¥ãŒç°å¢ã®ã¬ã€ã¢ãŠãæ
å ±ãååŸ
const previewResponse = await this.client.app.getFormLayout({
app: appId,
preview: true // ãã¬ãã¥ãŒç°å¢ãæå®
});
LoggingUtils.debug('form', 'get_form_layout_preview_response', previewResponse);
// ãã¬ãã¥ãŒç°å¢ããååŸããããšã瀺ãæ
å ±ã远å
return {
...previewResponse,
preview: true,
message: 'ãã®ã¬ã€ã¢ãŠãæ
å ±ã¯ãã¬ãã¥ãŒç°å¢ããååŸãããŸãããã¢ããªããããã€ããã«ã¯ deploy_app ããŒã«ã䜿çšããŠãã ããã'
};
}
// ãã®ä»ã®ãšã©ãŒã¯éåžžéãåŠç
throw error;
}
} catch (error) {
this.handleKintoneError(error, `get form layout for app ${appId}`);
}
}
/**
* ã¢ããªã«ãã£ãŒã«ãã远å
* @param {number} appId ã¢ããªID
* @param {Object} properties ãã£ãŒã«ãããããã£
* @returns {Promise<Object>} 远å çµæ
*/
async addFields(appId, properties) {
try {
LoggingUtils.info('form', 'add_fields', {
appId,
propertyCount: Object.keys(properties || {}).length
});
// æ¢åã®ãã£ãŒã«ãæ
å ±ãååŸ
let existingFields;
try {
existingFields = await this.getFormFields(appId);
LoggingUtils.debug('form', 'existing_field_count', {
appId,
count: Object.keys(existingFields.properties || {}).length
});
} catch (error) {
LoggingUtils.warn('form', 'existing_field_fetch_failed', {
appId,
message: error.message
});
existingFields = { properties: {} };
}
// æ¢åã®ãã£ãŒã«ãã³ãŒãã®ãªã¹ããäœæ
const existingFieldCodes = Object.keys(existingFields.properties || {});
LoggingUtils.debug('form', 'existing_field_codes', {
appId,
codes: existingFieldCodes
});
// 倿æžã¿ã®ããããã£ãæ ŒçŽããæ°ãããªããžã§ã¯ã
const convertedProperties = {};
const warnings = [];
// äœ¿çšæžã¿ãã£ãŒã«ãã³ãŒãã®ãªã¹ãïŒæ¢å + æ°èŠè¿œå æžã¿ïŒ
const usedFieldCodes = [...existingFieldCodes];
// ãã£ãŒã«ãã³ãŒãã®èªåçæé¢æ°
function generateFieldCode(label) {
if (!label) return '';
// ã©ãã«ãã䜿çšå¯èœãªæåã®ã¿ãæœåº
let code = label;
// è±æ°åãã²ãããªãã«ã¿ã«ããæŒ¢åãèš±å¯ãããèšå·ä»¥å€ãåé€
code = code.replace(/[^a-zA-Z0-9ã-ãã¡-ã¶ãŒäž-éŸ ã
_ã»ïŒï¿¥]/g, '_');
// å
é ãæ°åã®å Žåãå
é ã« 'f_' ã远å
if (/^[0-9ïŒ-ïŒ]/.test(code)) {
code = 'f_' + code;
}
return code;
}
// ãã£ãŒã«ãã³ãŒãã®æŽåæ§ãã§ãã¯ãšããªããŒã·ã§ã³
for (const [propertyKey, fieldConfig] of Object.entries(properties)) {
// ãã£ãŒã«ãã³ãŒãã®ããªããŒã·ã§ã³
validateFieldCode(propertyKey);
// codeããããã£ã®ååšãã§ãã¯
if (!fieldConfig.code) {
// codeãæå®ãããŠããªãå Žåãlabelããèªåçæ
if (fieldConfig.label) {
fieldConfig.code = generateFieldCode(fieldConfig.label);
warnings.push(
`ãã£ãŒã«ã "${propertyKey}" ã® code ãæå®ãããŠããªããããlabel ããèªåçæããŸãã: "${fieldConfig.code}"`
);
} else if (propertyKey !== fieldConfig.code) {
// labelããªãå Žåã¯ããããã£ããŒãcodeãšããŠäœ¿çš
fieldConfig.code = generateFieldCode(propertyKey);
warnings.push(
`ãã£ãŒã«ã "${propertyKey}" ã® code ãæå®ãããŠããªããããããããã£ããŒããèªåçæããŸãã: "${fieldConfig.code}"`
);
} else {
throw new Error(
`ãã£ãŒã«ã "${propertyKey}" ã® code ããããã£ãæå®ãããŠããŸããã\n` +
`åãã£ãŒã«ãã«ã¯äžæã®ã³ãŒããæå®ããå¿
èŠããããŸãã\n` +
`䜿çšå¯èœãªæå: ã²ãããªãã«ã¿ã«ããæŒ¢åãè±æ°åãèšå·(_ã»ïŒï¿¥)`
);
}
}
// ãã£ãŒã«ãã³ãŒãã®éè€ãã§ãã¯
if (usedFieldCodes.includes(fieldConfig.code)) {
// éè€ããå Žåãæ°ãããã£ãŒã«ãã³ãŒããçæ
const originalCode = fieldConfig.code;
let newCode = originalCode;
let suffix = 1;
// äžæã®ãã£ãŒã«ãã³ãŒãã«ãªããŸã§æ¥å°ŸèŸã远å
while (usedFieldCodes.includes(newCode)) {
newCode = `${originalCode}_${suffix}`;
suffix++;
}
// ãã£ãŒã«ãã³ãŒããæŽæ°
fieldConfig.code = newCode;
// èŠåã¡ãã»ãŒãžãèšé²
warnings.push(
`ãã£ãŒã«ãã³ãŒãã®éè€ãèªåä¿®æ£ããŸãã: "${originalCode}" â "${fieldConfig.code}"\n` +
`kintoneã®ä»æ§ã«ããããã£ãŒã«ãã³ãŒãã¯ã¢ããªå
ã§äžæã§ããå¿
èŠããããŸãã`
);
}
// äœ¿çšæžã¿ãªã¹ãã«è¿œå
usedFieldCodes.push(fieldConfig.code);
// ããããã£ããŒãšcodeã®äžèŽãã§ãã¯
if (fieldConfig.code !== propertyKey) {
// äžäžèŽã®å Žåã¯èŠåãèšé²ããæ£ããããŒã§ããããã£ã远å
warnings.push(
`ãã£ãŒã«ãã³ãŒãã®äžäžèŽãèªåä¿®æ£ããŸãã: ããããã£ã㌠"${propertyKey}" â ãã£ãŒã«ãã³ãŒã "${fieldConfig.code}"\n` +
`kintone APIã®ä»æ§ã«ãããããããã£ããŒãšãã£ãŒã«ãã³ãŒãã¯å®å
šã«äžèŽããŠããå¿
èŠããããŸãã`
);
// å
ã®ããããã£ããŒãlabelãšããŠä¿åïŒããlabelãæªèšå®ã®å ŽåïŒ
if (!fieldConfig.label) {
fieldConfig.label = propertyKey;
}
// æ£ããããŒã§ããããã£ã远å
convertedProperties[fieldConfig.code] = fieldConfig;
} else {
// äžèŽããŠããå Žåã¯ãã®ãŸãŸè¿œå
convertedProperties[propertyKey] = fieldConfig;
}
// éžæè¢ãã£ãŒã«ãã®optionsã®èªåä¿®æ£ãšããªããŒã·ã§ã³
if (fieldConfig.type && fieldConfig.options &&
FIELD_TYPES_REQUIRING_OPTIONS.includes(fieldConfig.type)) {
// éžæè¢ãã£ãŒã«ãã®optionsãèªåä¿®æ£
const { warnings: optionsWarnings, keyChanges } = autoCorrectOptions(fieldConfig.type, fieldConfig.options);
if (optionsWarnings.length > 0) {
warnings.push(...optionsWarnings);
}
// ããŒåã®å€æŽããã£ãå Žåãoptionsãªããžã§ã¯ããåæ§ç¯
if (Object.keys(keyChanges).length > 0) {
const newOptions = {};
for (const [key, value] of Object.entries(fieldConfig.options)) {
// 倿Žå¯Ÿè±¡ã®ããŒã®å Žåã¯æ°ããããŒåã䜿çš
const newKey = keyChanges[key] || key;
newOptions[newKey] = value;
}
fieldConfig.options = newOptions;
}
// ä¿®æ£åŸã®optionsãããªããŒã·ã§ã³
validateOptions(fieldConfig.type, fieldConfig.options);
}
// åäœäœçœ®ã®èªåä¿®æ£ãé©çš
if (fieldConfig.type) {
// validateField颿°ã䜿çšããŠèªåä¿®æ£ãé©çš
const correctedField = validateField(fieldConfig);
// ä¿®æ£ããããã£ãŒã«ãã§çœ®ãæã
if (fieldConfig.code === propertyKey) {
convertedProperties[propertyKey] = correctedField;
} else {
convertedProperties[fieldConfig.code] = correctedField;
}
// èªåä¿®æ£ã®çµæããã°ã«åºå
if (fieldConfig.type === "NUMBER" ||
(fieldConfig.type === "CALC" && fieldConfig.format === "NUMBER")) {
if (fieldConfig.unit && !fieldConfig.unitPosition && correctedField.unitPosition) {
warnings.push(
`ãã£ãŒã«ã "${fieldConfig.code}" ã® unitPosition ã "${correctedField.unitPosition}" ã«èªåèšå®ããŸããã`
);
}
}
// SUBTABLEãã£ãŒã«ãã®ç¹å¥ãªåŠç
if (fieldConfig.type === SUBTABLE_FIELD_TYPE) {
// fieldsããããã£ã®ååšãã§ãã¯
if (!fieldConfig.fields) {
throw new Error(
`ããŒãã«ãã£ãŒã«ã "${propertyKey}" ã«ã¯ fields ããããã£ã®æå®ãå¿
é ã§ãã\n` +
`ããŒãã«å
ã®ãã£ãŒã«ããå®çŸ©ãããªããžã§ã¯ããæå®ããŠãã ããã`
);
}
// fieldsã®åœ¢åŒãã§ãã¯
if (typeof fieldConfig.fields !== 'object' || Array.isArray(fieldConfig.fields)) {
throw new Error(
`ããŒãã«ãã£ãŒã«ã "${propertyKey}" ã® fields ã¯ãªããžã§ã¯ã圢åŒã§æå®ããå¿
èŠããããŸãã\n` +
`äŸ: "fields": { "field1": { "type": "SINGLE_LINE_TEXT", "code": "field1", "label": "ããã¹ã1" } }`
);
}
// ããŒãã«å
ã®äœ¿çšæžã¿ãã£ãŒã«ãã³ãŒãã®ãªã¹ã
const usedSubtableFieldCodes = [];
// ããŒãã«å
ã®åãã£ãŒã«ãããã§ãã¯
for (const [fieldKey, fieldDef] of Object.entries(fieldConfig.fields)) {
// ãã£ãŒã«ãã³ãŒãã®ããªããŒã·ã§ã³
validateFieldCode(fieldKey);
// codeããããã£ã®ååšãã§ãã¯
if (!fieldDef.code) {
// codeãæå®ãããŠããªãå Žåãlabelããèªåçæ
if (fieldDef.label) {
fieldDef.code = generateFieldCode(fieldDef.label);
warnings.push(
`ããŒãã« "${propertyKey}" å
ã®ãã£ãŒã«ã "${fieldKey}" ã® code ãæå®ãããŠããªããããlabel ããèªåçæããŸãã: "${fieldDef.code}"`
);
} else if (fieldKey !== fieldDef.code) {
// labelããªãå Žåã¯ããããã£ããŒãcodeãšããŠäœ¿çš
fieldDef.code = generateFieldCode(fieldKey);
warnings.push(
`ããŒãã« "${propertyKey}" å
ã®ãã£ãŒã«ã "${fieldKey}" ã® code ãæå®ãããŠããªããããããããã£ããŒããèªåçæããŸãã: "${fieldDef.code}"`
);
} else {
throw new Error(
`ããŒãã« "${propertyKey}" å
ã®ãã£ãŒã«ã "${fieldKey}" ã® code ããããã£ãæå®ãããŠããŸããã\n` +
`åãã£ãŒã«ãã«ã¯äžæã®ã³ãŒããæå®ããå¿
èŠããããŸãã\n` +
`䜿çšå¯èœãªæå: ã²ãããªãã«ã¿ã«ããæŒ¢åãè±æ°åãèšå·(_ã»ïŒï¿¥)`
);
}
}
// ããŒãã«å
ã®ãã£ãŒã«ãã³ãŒãã®éè€ãã§ãã¯
if (usedSubtableFieldCodes.includes(fieldDef.code)) {
// éè€ããå Žåãæ°ãããã£ãŒã«ãã³ãŒããçæ
const originalCode = fieldDef.code;
let newCode = originalCode;
let suffix = 1;
// äžæã®ãã£ãŒã«ãã³ãŒãã«ãªããŸã§æ¥å°ŸèŸã远å
while (usedSubtableFieldCodes.includes(newCode)) {
newCode = `${originalCode}_${suffix}`;
suffix++;
}
// ãã£ãŒã«ãã³ãŒããæŽæ°
fieldDef.code = newCode;
// èŠåã¡ãã»ãŒãžãèšé²
warnings.push(
`ããŒãã« "${propertyKey}" å
ã®ãã£ãŒã«ãã³ãŒãã®éè€ãèªåä¿®æ£ããŸãã: "${originalCode}" â "${fieldDef.code}"\n` +
`kintoneã®ä»æ§ã«ããããã£ãŒã«ãã³ãŒãã¯ããŒãã«å
ã§äžæã§ããå¿
èŠããããŸãã`
);
}
// äœ¿çšæžã¿ãªã¹ãã«è¿œå
usedSubtableFieldCodes.push(fieldDef.code);
// ããããã£ããŒãšcodeã®äžèŽãã§ãã¯
if (fieldDef.code !== fieldKey) {
warnings.push(
`ããŒãã« "${propertyKey}" å
ã®ãã£ãŒã«ãã³ãŒãã®äžäžèŽãèªåä¿®æ£ããŸãã: ` +
`ããããã£ã㌠"${fieldKey}" â ãã£ãŒã«ãã³ãŒã "${fieldDef.code}"\n` +
`kintone APIã®ä»æ§ã«ãããããããã£ããŒãšãã£ãŒã«ãã³ãŒãã¯å®å
šã«äžèŽããŠããå¿
èŠããããŸãã`
);
// å
ã®ããããã£ããŒãlabelãšããŠä¿åïŒããlabelãæªèšå®ã®å ŽåïŒ
if (!fieldDef.label) {
fieldDef.label = fieldKey;
}
// æ£ããããŒã§ãã£ãŒã«ãã远å
fieldConfig.fields[fieldDef.code] = fieldDef;
delete fieldConfig.fields[fieldKey];
}
// typeããããã£ã®ååšãã§ãã¯
if (!fieldDef.type) {
throw new Error(
`ããŒãã« "${propertyKey}" å
ã®ãã£ãŒã«ã "${fieldKey}" ã® type ããããã£ãæå®ãããŠããŸããã`
);
}
// ããŒãã«å
ã®éžæè¢ãã£ãŒã«ãã®optionsã®èªåä¿®æ£ãšããªããŒã·ã§ã³
if (fieldDef.options && FIELD_TYPES_REQUIRING_OPTIONS.includes(fieldDef.type)) {
// éžæè¢ãã£ãŒã«ãã®optionsãèªåä¿®æ£
const { warnings: optionsWarnings, keyChanges } = autoCorrectOptions(fieldDef.type, fieldDef.options);
if (optionsWarnings.length > 0) {
warnings.push(...optionsWarnings.map(w => `ããŒãã« "${propertyKey}" å
: ${w}`));
}
// ããŒåã®å€æŽããã£ãå Žåãoptionsãªããžã§ã¯ããåæ§ç¯
if (Object.keys(keyChanges).length > 0) {
const newOptions = {};
for (const [key, value] of Object.entries(fieldDef.options)) {
// 倿Žå¯Ÿè±¡ã®ããŒã®å Žåã¯æ°ããããŒåã䜿çš
const newKey = keyChanges[key] || key;
newOptions[newKey] = value;
}
fieldDef.options = newOptions;
}
// ä¿®æ£åŸã®optionsãããªããŒã·ã§ã³
validateOptions(fieldDef.type, fieldDef.options);
}
}
}
}
}
// èŠåã¡ãã»ãŒãžã衚瀺
if (warnings.length > 0) {
warnings.forEach((warning) => {
LoggingUtils.warn('form', 'field_addition_warning', { appId, warning });
});
}
const response = await this.client.app.addFormFields({
app: appId,
properties: convertedProperties,
revision: -1 // ææ°ã®ãªããžã§ã³ã䜿çš
});
LoggingUtils.debug('form', 'add_fields_response', response);
// èŠåã¡ãã»ãŒãžãå«ããæ¡åŒµã¬ã¹ãã³ã¹ãè¿ã
return {
...response,
warnings: warnings.length > 0 ? warnings : undefined
};
} catch (error) {
this.handleKintoneError(error, `add fields to app ${appId}`);
}
}
/**
* ãã©ãŒã ã¬ã€ã¢ãŠããæŽæ°
* @param {number} appId ã¢ããªID
* @param {Array} layout ã¬ã€ã¢ãŠãé
å
* @param {number} revision ãªããžã§ã³çªå·ïŒçç¥æã¯ææ°ïŒ
* @returns {Promise<Object>} æŽæ°çµæ
*/
async updateFormLayout(appId, layout, revision = -1) {
try {
LoggingUtils.info('form', 'update_form_layout', { appId, revision });
LoggingUtils.debug('form', 'update_form_layout_payload', layout);
// ã¬ã€ã¢ãŠãã®ããªããŒã·ã§ã³
validateFormLayout(layout);
// åèŠçŽ ã®ãµã€ãºèšå®ã®ããªããŒã·ã§ã³
const validateLayoutElementSizes = (items) => {
items.forEach(item => {
if (item.type === "ROW" && item.fields) {
item.fields.forEach(field => {
if (field.size) {
validateFieldSize(field.size);
}
});
} else if (item.type === "GROUP" && item.layout) {
validateLayoutElementSizes(item.layout);
}
});
};
validateLayoutElementSizes(layout);
// ãã©ãŒã ãã£ãŒã«ãæ
å ±ãååŸ
let formFields = null;
try {
const fieldsResponse = await this.getFormFields(appId);
formFields = fieldsResponse.properties || {};
LoggingUtils.debug('form', 'form_field_count_for_layout', {
appId,
count: Object.keys(formFields).length
});
} catch (error) {
LoggingUtils.warn('form', 'form_field_fetch_failed_for_layout', {
appId,
message: error.message
});
}
// äžè¶³ããŠãããã£ãŒã«ãã®èªå远å ã®æå¹/ç¡å¹ãå¶åŸ¡ãããã©ã°
const autoAddMissingFields = false; // äžè¶³ããŠãããã£ãŒã«ãã®èªå远å ãç¡å¹å
// ã¬ã€ã¢ãŠãã«å«ãŸããŠããªããã£ãŒã«ãããã§ãã¯
let layoutWithMissingFields = layout;
if (formFields) {
// ã¬ã€ã¢ãŠãã«å«ãŸããŠããªããã£ãŒã«ããæ€åº
const missingFields = validateFieldsInLayout(layout, formFields);
if (missingFields.length > 0) {
LoggingUtils.warn('form', 'layout_missing_fields', {
appId,
missingFields
});
if (autoAddMissingFields) {
// äžè¶³ããŠãããã£ãŒã«ããèªå远å
const { layout: fixedLayout, warnings } = addMissingFieldsToLayout(layout, formFields, true);
layoutWithMissingFields = fixedLayout;
// èŠåã¡ãã»ãŒãžãåºå
warnings.forEach((warning) => {
LoggingUtils.warn('form', 'auto_added_missing_field', { appId, warning });
});
} else {
LoggingUtils.warn('form', 'auto_add_missing_fields_disabled', { appId });
}
} else {
LoggingUtils.debug('form', 'layout_contains_all_fields', { appId });
}
}
// ã¬ã€ã¢ãŠãã®å¹
ãèªåè£æ£ïŒå
±éãŠãŒãã£ãªãã£ã䜿çšïŒ
let correctedLayout = layoutWithMissingFields;
if (formFields) {
const { layout: widthCorrectedLayout } = autoCorrectLayoutWidths(layoutWithMissingFields, formFields);
correctedLayout = widthCorrectedLayout;
LoggingUtils.debug('form', 'layout_width_corrected', { appId });
}
// GROUPãã£ãŒã«ãã® label ããããã£ãé€å»ãã颿°
const removeGroupLabels = (items) => {
// å
¥åãã§ãã¯
if (!items) {
LoggingUtils.warn('form', 'remove_group_labels_missing_items', { appId });
return [];
}
// Promiseãªããžã§ã¯ãã®å Žåã¯ãšã©ãŒãã°ãåºå
if (items instanceof Promise) {
LoggingUtils.warn('form', 'remove_group_labels_received_promise', { appId });
return [];
}
// é
åã§ãªãå Žåã¯é
åã«å€æ
if (!Array.isArray(items)) {
LoggingUtils.warn('form', 'remove_group_labels_non_array', {
appId,
itemType: typeof items
});
return [];
}
return items.map(item => {
if (!item) {
LoggingUtils.warn('form', 'remove_group_labels_null_item', { appId });
return null;
}
if (item.type === "GROUP") {
// label ããããã£ãåé€ããæ°ãããªããžã§ã¯ããäœæ
const newItem = { ...item };
delete newItem.label;
// layout ããããã£ãååšããå Žåã¯ååž°çã«åŠç
if (newItem.layout) {
// Promiseãªããžã§ã¯ãã®å Žåã¯ãšã©ãŒãã°ãåºå
if (newItem.layout instanceof Promise) {
LoggingUtils.warn('form', 'group_layout_contains_promise', { appId, groupCode: newItem.code });
newItem.layout = [];
} else if (!Array.isArray(newItem.layout)) {
// é
åã§ãªãå Žåã¯é
åã«å€æ
LoggingUtils.warn('form', 'group_layout_not_array', { appId, groupCode: newItem.code });
// 空ã§ãªãå€ã®å Žåã®ã¿é
åã«å€æ
newItem.layout = newItem.layout ? [newItem.layout] : [];
}
newItem.layout = removeGroupLabels(newItem.layout);
}
return newItem;
} else if (item.type === "ROW" && item.fields) {
// fieldsãé
åã§ãªãå Žåã¯é
åã«å€æ
if (!Array.isArray(item.fields)) {
LoggingUtils.warn('form', 'row_fields_not_array', { appId });
item.fields = item.fields ? [item.fields] : [];
}
// ROWå
ã®ãã£ãŒã«ããåŠçïŒå¿µã®ããïŒ
return {
...item,
fields: item.fields.map(field => {
if (!field) {
LoggingUtils.warn('form', 'row_contains_null_field', { appId });
return null;
}
if (field.type === "GROUP") {
const newField = { ...field };
delete newField.label;
return newField;
}
return field;
}).filter(Boolean) // nullãundefinedãé€å€
};
}
return item;
}).filter(Boolean); // nullãundefinedãé€å€
};
// ã¬ã€ã¢ãŠãæ
å ±ãã GROUPãã£ãŒã«ãã® label ããããã£ãé€å»
const layoutWithoutGroupLabels = removeGroupLabels(correctedLayout);
const params = {
app: appId,
layout: layoutWithoutGroupLabels,
revision: revision
};
// ãªã¯ãšã¹ããã©ã¡ãŒã¿ã®è©³çްããããã°ãã°ã«åºå
LoggingUtils.debug('form', 'update_form_layout_request', {
app: params.app,
revision: params.revision
});
LoggingUtils.debug('form', 'update_form_layout_request_layout', params.layout);
const response = await this.client.app.updateFormLayout(params);
LoggingUtils.debug('form', 'update_form_layout_response', response);
return response;
} catch (error) {
this.handleKintoneError(error, `update form layout for app ${appId}`);
}
}
/**
* ãã©ãŒã ãã£ãŒã«ããæŽæ°
* @param {number} appId ã¢ããªID
* @param {Object} properties ãã£ãŒã«ãããããã£
* @param {number} revision ãªããžã§ã³çªå·ïŒçç¥æã¯ææ°ïŒ
* @returns {Promise<Object>} æŽæ°çµæ
*/
async updateFormFields(appId, properties, revision = -1) {
try {
LoggingUtils.info('form', 'update_form_fields', { appId, revision, propertyCount: Object.keys(properties || {}).length });
LoggingUtils.debug('form', 'update_form_fields_payload', properties);
// æ¢åã®ãã£ãŒã«ãæ
å ±ãååŸ
let existingFields;
try {
existingFields = await this.getFormFields(appId);
LoggingUtils.debug('form', 'existing_field_count_for_update', {
appId,
count: Object.keys(existingFields.properties || {}).length
});
} catch (error) {
LoggingUtils.error('form', 'existing_fields_fetch_failed_for_update', error, { appId });
throw new Error(`æ¢åã®ãã£ãŒã«ãæ
å ±ãååŸã§ããŸããã§ãããã¢ããªID: ${appId}`);
}
// æ¢åã®ãã£ãŒã«ãã³ãŒãã®ãªã¹ããäœæ
const existingFieldCodes = Object.keys(existingFields.properties || {});
LoggingUtils.debug('form', 'existing_field_codes_for_update', {
appId,
codes: existingFieldCodes
});
// æŽæ°å¯Ÿè±¡ã®ãã£ãŒã«ããååšããããã§ãã¯
for (const fieldCode of Object.keys(properties)) {
if (!existingFieldCodes.includes(fieldCode)) {
throw new Error(`ãã£ãŒã«ã "${fieldCode}" ã¯ååšããŸãããæŽæ°å¯Ÿè±¡ã®ãã£ãŒã«ãã¯æ¢åã®ãã£ãŒã«ãã§ããå¿
èŠããããŸãã`);
}
}
// ãã£ãŒã«ãã®ããªããŒã·ã§ã³
for (const [fieldCode, fieldConfig] of Object.entries(properties)) {
// ãã£ãŒã«ãã³ãŒãã®ããªããŒã·ã§ã³
validateFieldCode(fieldCode);
// ãã£ãŒã«ãã¿ã€ããæå®ãããŠããªãå Žåã¯ãšã©ãŒ
if (!fieldConfig.type) {
throw new Error(`ãã£ãŒã«ã "${fieldCode}" ã«ã¯ã¿ã€ã(type)ã®æå®ãå¿
é ã§ãã`);
}
// åäœäœçœ®ã®èªåä¿®æ£ãé©çš
if (fieldConfig.type) {
// validateField颿°ã䜿çšããŠèªåä¿®æ£ãé©çš
const correctedField = validateField(fieldConfig);
// ä¿®æ£ããããã£ãŒã«ãã§çœ®ãæã
properties[fieldCode] = correctedField;
// èªåä¿®æ£ã®çµæããã°ã«åºå
if (fieldConfig.type === "NUMBER" ||
(fieldConfig.type === "CALC" && fieldConfig.format === "NUMBER")) {
if (fieldConfig.unit && !fieldConfig.unitPosition && correctedField.unitPosition) {
LoggingUtils.debug('form', 'unit_position_auto_set', {
appId,
fieldCode,
unitPosition: correctedField.unitPosition
});
}
}
}
}
const params = {
app: appId,
properties: properties,
revision: revision
};
const response = await this.client.app.updateFormFields(params);
LoggingUtils.debug('form', 'update_form_fields_response', response);
return response;
} catch (error) {
this.handleKintoneError(error, `update form fields for app ${appId}`);
}
}
/**
* ãã©ãŒã ãã£ãŒã«ããåé€
* @param {number} appId ã¢ããªID
* @param {Array<string>} fields åé€ãããã£ãŒã«ãã³ãŒãã®é
å
* @param {number} revision ãªããžã§ã³çªå·ïŒçç¥æã¯ææ°ïŒ
* @returns {Promise<Object>} åé€çµæ
*/
async deleteFormFields(appId, fields, revision = -1) {
try {
LoggingUtils.info('form', 'delete_form_fields', { appId, revision, fieldCount: fields?.length || 0 });
LoggingUtils.debug('form', 'delete_form_fields_payload', fields);
const params = {
app: appId,
fields: fields,
revision: revision
};
const response = await this.client.app.deleteFormFields(params);
LoggingUtils.debug('form', 'delete_form_fields_response', response);
return response;
} catch (error) {
this.handleKintoneError(error, `delete form fields for app ${appId}`);
}
}
}