UNPKG

@wishcbg/bobcode

Version:

基於店家同步功能的需求,讓店家能夠在不同的店家之間同步各項功能設定,並且能夠檢視各項功能的異動情況。 規劃出一套店家設定即程式碼的機制,讓店家能夠以 config file (.sac) 來定義各項功能設定,並且能夠還原店家各項功能設定。 暫且稱這個機制為「Settings as Code」「店家設定即程式碼」。

234 lines (233 loc) 9.88 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.applyChange = void 0; const jsonpath_plus_1 = require("jsonpath-plus"); const bobAPI_1 = __importDefault(require("./bobAPI")); const index_1 = require("../resources/index"); const error_1 = require("./error"); // main flow const applyChange = async (change, bobBaseUrl, bobShopId, bobToken, forceRemoveUnresolvedLogicIds) => { try { //#region 取得boblogic對應表 const bobApi = new bobAPI_1.default({ baseUrl: bobBaseUrl, token: bobToken, shopId: bobShopId, }); const bobLogicIdSetMap = await (async () => { const bobLogicIdSet = await bobApi.findLogicIdSet(); return new Map(bobLogicIdSet.map((item) => { return [item.logicId, item.resourceId]; })); })(); //#endregion //#region 根據每個change 依序處理 const { resourceType, operation, logicId, description, diffs, payload, } = change; const resourceSchema = index_1.allResourceSchemas[resourceType]; // 取得對應的 apiPath與 transformMethod // 可考慮重構 const { transformMethod, baseApiPath, } = (() => { let transformMethod = null; let baseApiPath = null; if (resourceSchema.relationType === 'single') { transformMethod = resourceSchema.applySingleUpdateApiBodyTransform; baseApiPath = resourceSchema.applySingleUpdateApiPath; } if (resourceSchema.relationType === 'multi') { if (operation === 'create') { transformMethod = resourceSchema.applyCreateApiBodyTransform; baseApiPath = resourceSchema.applyCreateApiPath; } if (operation === 'update') { transformMethod = resourceSchema.applyUpdateApiBodyTransform; baseApiPath = resourceSchema.applyUpdateApiPath; } if (operation === 'delete') { transformMethod = resourceSchema.applyDestroyApiBodyTransform; baseApiPath = resourceSchema.applyDestroyApiPath; } } return { transformMethod, baseApiPath, }; })(); if (!baseApiPath) { throw new error_1.BadRequestError(`找不到對應的apiPath: ${resourceType}`, 'APPLY_CHANGES_FAILED_WRONG_API_PATH'); } // 1. 將logicId 轉換成對應的resourceId const logicIdRelationFields = resourceSchema.relationResources .map((resource) => { return { settingSchemaField: resource.settingSchemaField, logicIdToResourceIdNewField: resource.logicIdToResourceIdNewField || resource.settingSchemaField, useLogicIdToResourceId: resource.useLogicIdToResourceId ?? true, acceptsNullValue: resource.acceptsNullValue ?? false, }; }); // 這個要改名 // 處理acceptsNullValue要放在這裡 const transformPayloadLogicId = (() => { if (!payload) { return {}; } const transformedBodyAfterReplacingLogicIds = JSON.parse(JSON.stringify(payload)); logicIdRelationFields.forEach(({ settingSchemaField: path, logicIdToResourceIdNewField: newPath, useLogicIdToResourceId, acceptsNullValue, }) => { (0, jsonpath_plus_1.JSONPath)({ path, json: transformedBodyAfterReplacingLogicIds, callback: (value, payloadType, { parent, }) => { const lastPath = path.split('.').pop(); const newLastPath = newPath.split('.').pop(); if (lastPath === undefined || newLastPath === undefined) { throw new error_1.BadRequestError(`無法處理的path: ${path}`, 'APPLY_CHANGES_FAILED_WRONG_PATH'); } if (value === null) { if (acceptsNullValue) { delete parent[lastPath]; parent[newLastPath] = null; } delete parent[lastPath]; return; } // 如果不是logicId,則不進行轉換 if (!useLogicIdToResourceId) { delete parent[lastPath]; parent[newLastPath] = value; return; } if (Array.isArray(value)) { const resourceIds = []; value.forEach((one) => { const resourceId = bobLogicIdSetMap.get(one); if (resourceId) { resourceIds.push(resourceId); } else { if (!forceRemoveUnresolvedLogicIds) { throw new error_1.BadRequestError(`找不到對應的resourceId: ${lastPath}`, 'APPLY_CHANGES_FAILED_UNRESOLVED_LOGIC_ID'); } } }); delete parent[lastPath]; parent[newLastPath] = resourceIds; } else { const resourceId = bobLogicIdSetMap.get(value); if (resourceId) { // 將logicId轉換成resourceId delete parent[lastPath]; parent[newLastPath] = resourceId; } else { if (!forceRemoveUnresolvedLogicIds) { throw new error_1.BadRequestError(`找不到對應的resourceId: ${lastPath}`, 'APPLY_CHANGES_FAILED_UNRESOLVED_LOGIC_ID'); } delete parent[lastPath]; } } } }); }); return transformedBodyAfterReplacingLogicIds; })(); // 2. 查看是否有對應的transform,進行轉換 const { apiPath, apiBody, } = await (async () => { const resourceId = bobLogicIdSetMap.get(logicId); if (transformMethod) { return await transformMethod({ shopId: bobShopId, resourceId: resourceId, }, transformPayloadLogicId, bobApi); } else { return { apiPath: baseApiPath.replace(':shopId', bobShopId).replace(':resourceId', resourceId || ''), apiBody: transformPayloadLogicId, }; } })(); // 3. 根據apiPath、method、body 進行請求 const httpMethod = (() => { if (resourceSchema.relationType === 'single') { return 'put'; } if (operation === 'create') { return 'post'; } else if (operation === 'update') { return 'put'; } else if (operation === 'delete') { return 'delete'; } throw new error_1.BadRequestError(`不支援的操作: ${operation}`, 'APPLY_CHANGES_FAILED_WRONG_OPERATION'); })(); const response = await bobApi.applyChanges(apiPath, httpMethod, apiBody); // 4. 根據response 進行對應的處理 ? 好像目前沒有要處理的 // 5. 將要修改logicId,存入boblogic對應表 if (operation === 'create') { await bobApi.upsertLogicIdSet([ { resourceType, logicId, resourceId: response.data.id, } ]); } else if (operation === 'delete') { await bobApi.upsertLogicIdSet([ { resourceType, logicId, resourceId: null, } ]); } // 6. 回傳結果 return { status: 'success', errorMessage: null, errorCode: null, resourceType, operation, logicId, description, diffs, payload: apiBody }; //#endregion } catch (error) { console.error(`處理變更 ${change.logicId} 時發生錯誤:`, error); const { message, code } = normalizeError(error); const { resourceType, operation, logicId, description, diffs, payload } = change; return { status: 'failed', errorMessage: message, errorCode: code, resourceType, operation, logicId, description, diffs, payload, }; } }; exports.applyChange = applyChange; function normalizeError(err) { if (err instanceof Error) { return { message: err.message, code: err.code ?? 'UNKNOWN_ERROR', }; } return { message: String(err), code: 'UNKNOWN_ERROR', }; }