@exabugs/dynamodb-client 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/LICENSE +21 -0
  3. package/README.md +283 -0
  4. package/dist/client/Collection.d.ts +57 -0
  5. package/dist/client/Collection.d.ts.map +1 -0
  6. package/dist/client/Collection.js +174 -0
  7. package/dist/client/Collection.js.map +1 -0
  8. package/dist/client/Database.d.ts +35 -0
  9. package/dist/client/Database.d.ts.map +1 -0
  10. package/dist/client/Database.js +48 -0
  11. package/dist/client/Database.js.map +1 -0
  12. package/dist/client/DynamoClient.d.ts +43 -0
  13. package/dist/client/DynamoClient.d.ts.map +1 -0
  14. package/dist/client/DynamoClient.js +62 -0
  15. package/dist/client/DynamoClient.js.map +1 -0
  16. package/dist/client/FindCursor.d.ts +174 -0
  17. package/dist/client/FindCursor.d.ts.map +1 -0
  18. package/dist/client/FindCursor.js +256 -0
  19. package/dist/client/FindCursor.js.map +1 -0
  20. package/dist/client/aws-sigv4.d.ts +10 -0
  21. package/dist/client/aws-sigv4.d.ts.map +1 -0
  22. package/dist/client/aws-sigv4.js +54 -0
  23. package/dist/client/aws-sigv4.js.map +1 -0
  24. package/dist/client/index.cognito.d.ts +34 -0
  25. package/dist/client/index.cognito.d.ts.map +1 -0
  26. package/dist/client/index.cognito.js +30 -0
  27. package/dist/client/index.cognito.js.map +1 -0
  28. package/dist/client/index.d.ts +12 -0
  29. package/dist/client/index.d.ts.map +1 -0
  30. package/dist/client/index.iam.d.ts +34 -0
  31. package/dist/client/index.iam.d.ts.map +1 -0
  32. package/dist/client/index.iam.js +28 -0
  33. package/dist/client/index.iam.js.map +1 -0
  34. package/dist/client/index.js +12 -0
  35. package/dist/client/index.js.map +1 -0
  36. package/dist/client/index.token.d.ts +33 -0
  37. package/dist/client/index.token.d.ts.map +1 -0
  38. package/dist/client/index.token.js +28 -0
  39. package/dist/client/index.token.js.map +1 -0
  40. package/dist/dynamodb.d.ts +20 -0
  41. package/dist/dynamodb.d.ts.map +1 -0
  42. package/dist/dynamodb.js +31 -0
  43. package/dist/dynamodb.js.map +1 -0
  44. package/dist/errors.d.ts +100 -0
  45. package/dist/errors.d.ts.map +1 -0
  46. package/dist/errors.js +146 -0
  47. package/dist/errors.js.map +1 -0
  48. package/dist/index.d.ts +10 -0
  49. package/dist/index.d.ts.map +1 -0
  50. package/dist/index.js +7 -0
  51. package/dist/index.js.map +1 -0
  52. package/dist/integrations/react-admin/dataProvider.d.ts +59 -0
  53. package/dist/integrations/react-admin/dataProvider.d.ts.map +1 -0
  54. package/dist/integrations/react-admin/dataProvider.js +364 -0
  55. package/dist/integrations/react-admin/dataProvider.js.map +1 -0
  56. package/dist/integrations/react-admin/index.d.ts +24 -0
  57. package/dist/integrations/react-admin/index.d.ts.map +1 -0
  58. package/dist/integrations/react-admin/index.js +23 -0
  59. package/dist/integrations/react-admin/index.js.map +1 -0
  60. package/dist/integrations/react-admin/types.d.ts +47 -0
  61. package/dist/integrations/react-admin/types.d.ts.map +1 -0
  62. package/dist/integrations/react-admin/types.js +5 -0
  63. package/dist/integrations/react-admin/types.js.map +1 -0
  64. package/dist/logger.d.ts +61 -0
  65. package/dist/logger.d.ts.map +1 -0
  66. package/dist/logger.js +87 -0
  67. package/dist/logger.js.map +1 -0
  68. package/dist/scripts/repair-shadows.d.ts +3 -0
  69. package/dist/scripts/repair-shadows.d.ts.map +1 -0
  70. package/dist/scripts/repair-shadows.js +190 -0
  71. package/dist/scripts/repair-shadows.js.map +1 -0
  72. package/dist/server/handler.cjs +31378 -0
  73. package/dist/server/handler.cjs.map +7 -0
  74. package/dist/server/handler.d.ts +18 -0
  75. package/dist/server/handler.d.ts.map +1 -0
  76. package/dist/server/handler.js +435 -0
  77. package/dist/server/handler.js.map +1 -0
  78. package/dist/server/handler.zip +0 -0
  79. package/dist/server/index.d.ts +8 -0
  80. package/dist/server/index.d.ts.map +1 -0
  81. package/dist/server/index.js +8 -0
  82. package/dist/server/index.js.map +1 -0
  83. package/dist/server/operations/deleteMany.d.ts +18 -0
  84. package/dist/server/operations/deleteMany.d.ts.map +1 -0
  85. package/dist/server/operations/deleteMany.js +222 -0
  86. package/dist/server/operations/deleteMany.js.map +1 -0
  87. package/dist/server/operations/deleteOne.d.ts +17 -0
  88. package/dist/server/operations/deleteOne.d.ts.map +1 -0
  89. package/dist/server/operations/deleteOne.js +87 -0
  90. package/dist/server/operations/deleteOne.js.map +1 -0
  91. package/dist/server/operations/find.d.ts +18 -0
  92. package/dist/server/operations/find.d.ts.map +1 -0
  93. package/dist/server/operations/find.js +382 -0
  94. package/dist/server/operations/find.js.map +1 -0
  95. package/dist/server/operations/findMany.d.ts +13 -0
  96. package/dist/server/operations/findMany.d.ts.map +1 -0
  97. package/dist/server/operations/findMany.js +61 -0
  98. package/dist/server/operations/findMany.js.map +1 -0
  99. package/dist/server/operations/findManyReference.d.ts +18 -0
  100. package/dist/server/operations/findManyReference.d.ts.map +1 -0
  101. package/dist/server/operations/findManyReference.js +150 -0
  102. package/dist/server/operations/findManyReference.js.map +1 -0
  103. package/dist/server/operations/findOne.d.ts +14 -0
  104. package/dist/server/operations/findOne.d.ts.map +1 -0
  105. package/dist/server/operations/findOne.js +56 -0
  106. package/dist/server/operations/findOne.js.map +1 -0
  107. package/dist/server/operations/insertMany.d.ts +19 -0
  108. package/dist/server/operations/insertMany.d.ts.map +1 -0
  109. package/dist/server/operations/insertMany.js +243 -0
  110. package/dist/server/operations/insertMany.js.map +1 -0
  111. package/dist/server/operations/insertOne.d.ts +18 -0
  112. package/dist/server/operations/insertOne.d.ts.map +1 -0
  113. package/dist/server/operations/insertOne.js +85 -0
  114. package/dist/server/operations/insertOne.js.map +1 -0
  115. package/dist/server/operations/updateMany.d.ts +20 -0
  116. package/dist/server/operations/updateMany.d.ts.map +1 -0
  117. package/dist/server/operations/updateMany.js +316 -0
  118. package/dist/server/operations/updateMany.js.map +1 -0
  119. package/dist/server/operations/updateOne.d.ts +20 -0
  120. package/dist/server/operations/updateOne.d.ts.map +1 -0
  121. package/dist/server/operations/updateOne.js +159 -0
  122. package/dist/server/operations/updateOne.js.map +1 -0
  123. package/dist/server/query/converter.d.ts +85 -0
  124. package/dist/server/query/converter.d.ts.map +1 -0
  125. package/dist/server/query/converter.js +161 -0
  126. package/dist/server/query/converter.js.map +1 -0
  127. package/dist/server/query/index.d.ts +5 -0
  128. package/dist/server/query/index.d.ts.map +1 -0
  129. package/dist/server/query/index.js +5 -0
  130. package/dist/server/query/index.js.map +1 -0
  131. package/dist/server/shadow/config.d.ts +147 -0
  132. package/dist/server/shadow/config.d.ts.map +1 -0
  133. package/dist/server/shadow/config.js +162 -0
  134. package/dist/server/shadow/config.js.map +1 -0
  135. package/dist/server/shadow/differ.d.ts +42 -0
  136. package/dist/server/shadow/differ.d.ts.map +1 -0
  137. package/dist/server/shadow/differ.js +66 -0
  138. package/dist/server/shadow/differ.js.map +1 -0
  139. package/dist/server/shadow/generator.d.ts +104 -0
  140. package/dist/server/shadow/generator.d.ts.map +1 -0
  141. package/dist/server/shadow/generator.js +148 -0
  142. package/dist/server/shadow/generator.js.map +1 -0
  143. package/dist/server/shadow/index.d.ts +11 -0
  144. package/dist/server/shadow/index.d.ts.map +1 -0
  145. package/dist/server/shadow/index.js +11 -0
  146. package/dist/server/shadow/index.js.map +1 -0
  147. package/dist/server/shadow/types.d.ts +44 -0
  148. package/dist/server/shadow/types.d.ts.map +1 -0
  149. package/dist/server/shadow/types.js +2 -0
  150. package/dist/server/shadow/types.js.map +1 -0
  151. package/dist/server/types.d.ts +295 -0
  152. package/dist/server/types.d.ts.map +1 -0
  153. package/dist/server/types.js +7 -0
  154. package/dist/server/types.js.map +1 -0
  155. package/dist/server/utils/auth.d.ts +43 -0
  156. package/dist/server/utils/auth.d.ts.map +1 -0
  157. package/dist/server/utils/auth.js +123 -0
  158. package/dist/server/utils/auth.js.map +1 -0
  159. package/dist/server/utils/bulkOperations.d.ts +81 -0
  160. package/dist/server/utils/bulkOperations.d.ts.map +1 -0
  161. package/dist/server/utils/bulkOperations.js +147 -0
  162. package/dist/server/utils/bulkOperations.js.map +1 -0
  163. package/dist/server/utils/chunking.d.ts +96 -0
  164. package/dist/server/utils/chunking.d.ts.map +1 -0
  165. package/dist/server/utils/chunking.js +225 -0
  166. package/dist/server/utils/chunking.js.map +1 -0
  167. package/dist/server/utils/dynamodb.d.ts +41 -0
  168. package/dist/server/utils/dynamodb.d.ts.map +1 -0
  169. package/dist/server/utils/dynamodb.js +83 -0
  170. package/dist/server/utils/dynamodb.js.map +1 -0
  171. package/dist/server/utils/filter.d.ts +152 -0
  172. package/dist/server/utils/filter.d.ts.map +1 -0
  173. package/dist/server/utils/filter.js +270 -0
  174. package/dist/server/utils/filter.js.map +1 -0
  175. package/dist/server/utils/pagination.d.ts +27 -0
  176. package/dist/server/utils/pagination.d.ts.map +1 -0
  177. package/dist/server/utils/pagination.js +56 -0
  178. package/dist/server/utils/pagination.js.map +1 -0
  179. package/dist/server/utils/timestamps.d.ts +31 -0
  180. package/dist/server/utils/timestamps.d.ts.map +1 -0
  181. package/dist/server/utils/timestamps.js +84 -0
  182. package/dist/server/utils/timestamps.js.map +1 -0
  183. package/dist/server/utils/ttl.d.ts +17 -0
  184. package/dist/server/utils/ttl.d.ts.map +1 -0
  185. package/dist/server/utils/ttl.js +62 -0
  186. package/dist/server/utils/ttl.js.map +1 -0
  187. package/dist/server/utils/validation.d.ts +40 -0
  188. package/dist/server/utils/validation.d.ts.map +1 -0
  189. package/dist/server/utils/validation.js +54 -0
  190. package/dist/server/utils/validation.js.map +1 -0
  191. package/dist/shadows/config.d.ts +54 -0
  192. package/dist/shadows/config.d.ts.map +1 -0
  193. package/dist/shadows/config.js +95 -0
  194. package/dist/shadows/config.js.map +1 -0
  195. package/dist/shadows/differ.d.ts +42 -0
  196. package/dist/shadows/differ.d.ts.map +1 -0
  197. package/dist/shadows/differ.js +66 -0
  198. package/dist/shadows/differ.js.map +1 -0
  199. package/dist/shadows/generator.d.ts +63 -0
  200. package/dist/shadows/generator.d.ts.map +1 -0
  201. package/dist/shadows/generator.js +107 -0
  202. package/dist/shadows/generator.js.map +1 -0
  203. package/dist/shadows/index.d.ts +15 -0
  204. package/dist/shadows/index.d.ts.map +1 -0
  205. package/dist/shadows/index.js +17 -0
  206. package/dist/shadows/index.js.map +1 -0
  207. package/dist/shadows/types.d.ts +44 -0
  208. package/dist/shadows/types.d.ts.map +1 -0
  209. package/dist/shadows/types.js +2 -0
  210. package/dist/shadows/types.js.map +1 -0
  211. package/dist/types.d.ts +165 -0
  212. package/dist/types.d.ts.map +1 -0
  213. package/dist/types.js +2 -0
  214. package/dist/types.js.map +1 -0
  215. package/dist/ulid.d.ts +46 -0
  216. package/dist/ulid.d.ts.map +1 -0
  217. package/dist/ulid.js +66 -0
  218. package/dist/ulid.js.map +1 -0
  219. package/package.json +136 -0
  220. package/terraform/README.md +222 -0
  221. package/terraform/examples/advanced/README.md +129 -0
  222. package/terraform/examples/advanced/main.tf +158 -0
  223. package/terraform/examples/advanced/shadow.config.json +35 -0
  224. package/terraform/examples/advanced/variables.tf +28 -0
  225. package/terraform/examples/basic/README.md +53 -0
  226. package/terraform/examples/basic/main.tf +99 -0
  227. package/terraform/examples/basic/variables.tf +17 -0
  228. package/terraform/main.tf +159 -0
  229. package/terraform/outputs.tf +56 -0
  230. package/terraform/variables.tf +59 -0
@@ -0,0 +1,316 @@
1
+ /**
2
+ * updateMany 操作
3
+ * 複数レコードを一括更新する(JSON Merge Patch形式)
4
+ *
5
+ * 要件: 4.2, 4.4, 5.2, 5.3, 7.4, 13.1, 13.2, 13.3, 13.8, 13.12
6
+ */
7
+ import { BatchGetCommand, TransactWriteCommand } from '@aws-sdk/lib-dynamodb';
8
+ import { createLogger, generateShadowRecords, getResourceSchema, getShadowConfig, } from '../../index.js';
9
+ import { calculateShadowDiff, generateMainRecordSK } from '../shadow/index.js';
10
+ import { calculateTimeoutRisk, logBulkOperationComplete, logLargeBatchWarning, logPartialFailure, logPreparationTimeoutRisk, } from '../utils/bulkOperations.js';
11
+ import { calculateChunks, executeChunks } from '../utils/chunking.js';
12
+ import { executeDynamoDBOperation, getDBClient, getTableName, removeShadowKeys, } from '../utils/dynamodb.js';
13
+ import { addUpdateTimestamp } from '../utils/timestamps.js';
14
+ const logger = createLogger({ service: 'records-lambda' });
15
+ /**
16
+ * JSON Merge Patch (RFC 7396) を適用する
17
+ *
18
+ * @param target - 対象オブジェクト
19
+ * @param patch - パッチオブジェクト
20
+ * @returns マージされたオブジェクト
21
+ */
22
+ function applyJsonMergePatch(target, patch) {
23
+ const result = { ...target };
24
+ for (const [key, value] of Object.entries(patch)) {
25
+ if (value === null) {
26
+ delete result[key];
27
+ }
28
+ else if (typeof value === 'object' &&
29
+ !Array.isArray(value) &&
30
+ value !== null &&
31
+ typeof result[key] === 'object' &&
32
+ !Array.isArray(result[key]) &&
33
+ result[key] !== null) {
34
+ result[key] = applyJsonMergePatch(result[key], value);
35
+ }
36
+ else {
37
+ result[key] = value;
38
+ }
39
+ }
40
+ return result;
41
+ }
42
+ /**
43
+ * updateMany 操作を実行する
44
+ *
45
+ * 処理フロー:
46
+ * 1. BatchGetItemで既存レコードを取得
47
+ * 2. 各レコードにJSON Merge Patchを適用
48
+ * 3. updatedAtタイムスタンプを更新
49
+ * 4. 新しいシャドーSKを生成し、差分を計算
50
+ * 5. アイテム数を計算してチャンク分割(1 + 削除シャドー数 + 追加シャドー数)
51
+ * 6. 各チャンクをTransactWriteItemsで順次実行
52
+ * 7. 部分失敗をハンドリング
53
+ *
54
+ * @param resource - リソース名
55
+ * @param params - updateManyパラメータ
56
+ * @param requestId - リクエストID
57
+ * @returns 更新結果(成功レコード、失敗ID、エラー情報)
58
+ */
59
+ export async function handleUpdateMany(resource, params, requestId) {
60
+ const { ids, data: patchData } = params;
61
+ const startTime = Date.now();
62
+ logger.debug('Executing updateMany', {
63
+ requestId,
64
+ resource,
65
+ count: ids.length,
66
+ });
67
+ // IDが空の場合は空レスポンスを返す
68
+ if (ids.length === 0) {
69
+ return {
70
+ count: 0,
71
+ successIds: {},
72
+ failedIds: {},
73
+ errors: {},
74
+ };
75
+ }
76
+ // 大量レコード処理時の警告ログ出力(要件: 13.12)
77
+ logLargeBatchWarning('updateMany', ids.length, requestId, resource);
78
+ const dbClient = getDBClient();
79
+ const tableName = getTableName();
80
+ // BatchGetItemで既存レコードを取得
81
+ const keys = ids.map((id) => ({
82
+ PK: resource,
83
+ SK: generateMainRecordSK(id),
84
+ }));
85
+ const batchGetResult = await executeDynamoDBOperation(() => dbClient.send(new BatchGetCommand({
86
+ RequestItems: {
87
+ [tableName]: {
88
+ Keys: keys,
89
+ ConsistentRead: true,
90
+ },
91
+ },
92
+ })), 'BatchGetItem');
93
+ const existingItems = batchGetResult.Responses?.[tableName] || [];
94
+ // 存在するレコードのIDセットを作成
95
+ const existingIds = new Set(existingItems.map((item) => {
96
+ const data = item.data;
97
+ return data.id;
98
+ }));
99
+ // 存在しないIDを特定
100
+ const notFoundIds = ids.filter((id) => !existingIds.has(id));
101
+ // シャドー設定を取得(環境変数からキャッシュ付き)
102
+ const shadowConfig = getShadowConfig();
103
+ const shadowSchema = getResourceSchema(shadowConfig, resource);
104
+ const preparedRecords = [];
105
+ const preparationFailedIds = [];
106
+ const preparationErrors = [];
107
+ // 各レコードを準備
108
+ for (const item of existingItems) {
109
+ try {
110
+ const existingData = item.data;
111
+ const id = existingData.id;
112
+ const oldShadowKeys = existingData.__shadowKeys || [];
113
+ // JSON Merge Patchを適用
114
+ const mergedData = applyJsonMergePatch(removeShadowKeys(existingData), patchData);
115
+ // updatedAt を更新(タイムスタンプフィールド名は動的に取得)
116
+ const updatedData = addUpdateTimestamp({
117
+ ...mergedData,
118
+ id,
119
+ });
120
+ // 新しいシャドーレコードを生成
121
+ const newShadowRecords = generateShadowRecords(updatedData, shadowSchema);
122
+ const newShadowKeys = newShadowRecords.map((shadow) => shadow.SK);
123
+ // メインレコードのSKを生成
124
+ const mainSK = generateMainRecordSK(id);
125
+ preparedRecords.push({
126
+ id,
127
+ updatedData,
128
+ oldShadowKeys,
129
+ newShadowKeys,
130
+ mainSK,
131
+ });
132
+ }
133
+ catch (error) {
134
+ // 準備段階で失敗したレコードを記録
135
+ const existingData = item.data;
136
+ const failedId = existingData.id || 'unknown-id';
137
+ const errorMessage = error instanceof Error ? error.message : 'Unknown preparation error';
138
+ const errorCode = getPreparationErrorCode(error);
139
+ logger.error('Failed to prepare record for update', {
140
+ requestId,
141
+ recordId: failedId,
142
+ error: errorMessage,
143
+ errorCode,
144
+ });
145
+ // 準備失敗をエラーリストに追加
146
+ preparationFailedIds.push(failedId);
147
+ preparationErrors.push({
148
+ id: failedId,
149
+ code: errorCode,
150
+ message: errorMessage,
151
+ });
152
+ }
153
+ }
154
+ // アイテム数計算関数(1 + 削除シャドー数 + 追加シャドー数)
155
+ const getItemCount = (record) => {
156
+ const shadowDiff = calculateShadowDiff(record.oldShadowKeys, record.newShadowKeys);
157
+ return 1 + shadowDiff.toDelete.length + shadowDiff.toAdd.length;
158
+ };
159
+ // チャンク分割
160
+ const { chunks } = calculateChunks(preparedRecords, getItemCount);
161
+ // 準備段階の経過時間をチェック(要件: 13.12)
162
+ const preparationRiskInfo = calculateTimeoutRisk(startTime);
163
+ logPreparationTimeoutRisk(requestId, resource, preparedRecords.length, chunks.length, preparationRiskInfo);
164
+ // チャンク実行関数
165
+ const executeChunk = async (chunk) => {
166
+ const transactItems = [];
167
+ // チャンク内の各レコードをTransactItemsに変換
168
+ for (const record of chunk) {
169
+ // シャドー差分を計算
170
+ const shadowDiff = calculateShadowDiff(record.oldShadowKeys, record.newShadowKeys);
171
+ // メインレコードを更新
172
+ transactItems.push({
173
+ Put: {
174
+ TableName: tableName,
175
+ Item: {
176
+ PK: resource,
177
+ SK: record.mainSK,
178
+ data: {
179
+ ...record.updatedData,
180
+ __shadowKeys: record.newShadowKeys,
181
+ },
182
+ },
183
+ },
184
+ });
185
+ // 旧シャドーを削除
186
+ for (const shadowSK of shadowDiff.toDelete) {
187
+ transactItems.push({
188
+ Delete: {
189
+ TableName: tableName,
190
+ Key: {
191
+ PK: resource,
192
+ SK: shadowSK,
193
+ },
194
+ },
195
+ });
196
+ }
197
+ // 新シャドーを追加
198
+ for (const shadowSK of shadowDiff.toAdd) {
199
+ transactItems.push({
200
+ Put: {
201
+ TableName: tableName,
202
+ Item: {
203
+ PK: resource,
204
+ SK: shadowSK,
205
+ data: {},
206
+ },
207
+ },
208
+ });
209
+ }
210
+ }
211
+ // TransactWriteItemsを実行
212
+ await executeDynamoDBOperation(() => dbClient.send(new TransactWriteCommand({
213
+ TransactItems: transactItems,
214
+ })), 'TransactWriteItems');
215
+ // 成功したレコードを返す
216
+ return chunk;
217
+ };
218
+ // チャンクを順次実行
219
+ const { successRecords, failedIds: chunkFailedIds, errors: chunkErrors, } = await executeChunks(chunks, executeChunk, (record) => record.id);
220
+ // 統一レスポンス形式を構築
221
+ // successIds: インデックス付きオブジェクト(元の配列のインデックスとIDのマッピング)
222
+ const successIds = {};
223
+ const failedIdsMap = {};
224
+ const errorsMap = {};
225
+ // 成功したレコードのインデックスを特定
226
+ const successIdSet = new Set(successRecords.map((r) => r.id));
227
+ for (let i = 0; i < ids.length; i++) {
228
+ const id = ids[i];
229
+ if (successIdSet.has(id)) {
230
+ successIds[i] = id;
231
+ }
232
+ }
233
+ // 存在しないIDのインデックスを特定
234
+ for (let i = 0; i < ids.length; i++) {
235
+ const id = ids[i];
236
+ if (notFoundIds.includes(id)) {
237
+ failedIdsMap[i] = id;
238
+ errorsMap[i] = {
239
+ id,
240
+ code: 'ITEM_NOT_FOUND',
241
+ message: `Record not found: ${id}`,
242
+ };
243
+ }
244
+ }
245
+ // 準備失敗のインデックスを特定
246
+ for (let i = 0; i < ids.length; i++) {
247
+ const id = ids[i];
248
+ if (preparationFailedIds.includes(id)) {
249
+ failedIdsMap[i] = id;
250
+ const error = preparationErrors.find((e) => e.id === id);
251
+ if (error) {
252
+ errorsMap[i] = error;
253
+ }
254
+ }
255
+ }
256
+ // チャンク実行失敗のインデックスを特定
257
+ for (let i = 0; i < ids.length; i++) {
258
+ const id = ids[i];
259
+ if (chunkFailedIds.includes(id)) {
260
+ failedIdsMap[i] = id;
261
+ const error = chunkErrors.find((e) => e.id === id);
262
+ if (error) {
263
+ errorsMap[i] = {
264
+ id: error.id,
265
+ code: error.code,
266
+ message: error.message,
267
+ };
268
+ }
269
+ }
270
+ }
271
+ const count = Object.keys(successIds).length;
272
+ const allFailedIds = Object.values(failedIdsMap);
273
+ // 総実行時間を計算(要件: 13.12)
274
+ const completionRiskInfo = calculateTimeoutRisk(startTime);
275
+ logBulkOperationComplete('updateMany', requestId, resource, ids.length, count, allFailedIds.length, completionRiskInfo, {
276
+ updated: count,
277
+ notFound: notFoundIds.length,
278
+ preparationFailed: preparationFailedIds.length,
279
+ chunkExecutionFailed: chunkFailedIds.length,
280
+ });
281
+ // 部分失敗の場合、詳細なエラーサマリーをログ出力
282
+ if (allFailedIds.length > 0) {
283
+ logPartialFailure('updateMany', requestId, resource, ids.length, count, allFailedIds.length, Object.values(errorsMap).map((e) => e.code), {
284
+ notFoundCount: notFoundIds.length,
285
+ preparationFailures: preparationFailedIds.length,
286
+ chunkExecutionFailures: chunkFailedIds.length,
287
+ });
288
+ }
289
+ // 統一レスポンス形式で返却
290
+ return {
291
+ count,
292
+ successIds,
293
+ failedIds: failedIdsMap,
294
+ errors: errorsMap,
295
+ };
296
+ }
297
+ /**
298
+ * 準備段階のエラーからエラーコードを抽出する
299
+ *
300
+ * @param error - エラーオブジェクト
301
+ * @returns エラーコード
302
+ */
303
+ function getPreparationErrorCode(error) {
304
+ if (error && typeof error === 'object') {
305
+ // カスタムエラー
306
+ if ('code' in error && typeof error.code === 'string') {
307
+ return error.code;
308
+ }
309
+ // AWS SDK エラー
310
+ if ('name' in error && typeof error.name === 'string') {
311
+ return error.name;
312
+ }
313
+ }
314
+ return 'VALIDATION_ERROR';
315
+ }
316
+ //# sourceMappingURL=updateMany.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"updateMany.js","sourceRoot":"","sources":["../../../src/server/operations/updateMany.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAE9E,OAAO,EACL,YAAY,EACZ,qBAAqB,EACrB,iBAAiB,EACjB,eAAe,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE/E,OAAO,EACL,oBAAoB,EACpB,wBAAwB,EACxB,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EACL,wBAAwB,EACxB,WAAW,EACX,YAAY,EACZ,gBAAgB,GACjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5D,MAAM,MAAM,GAAG,YAAY,CAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC,CAAC;AAkB3D;;;;;;GAMG;AACH,SAAS,mBAAmB,CAC1B,MAA+B,EAC/B,KAA8B;IAE9B,MAAM,MAAM,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC;IAE7B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACjD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;QACrB,CAAC;aAAM,IACL,OAAO,KAAK,KAAK,QAAQ;YACzB,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YACrB,KAAK,KAAK,IAAI;YACd,OAAO,MAAM,CAAC,GAAG,CAAC,KAAK,QAAQ;YAC/B,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC3B,MAAM,CAAC,GAAG,CAAC,KAAK,IAAI,EACpB,CAAC;YACD,MAAM,CAAC,GAAG,CAAC,GAAG,mBAAmB,CAC/B,MAAM,CAAC,GAAG,CAA4B,EACtC,KAAgC,CACjC,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACtB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,QAAgB,EAChB,MAAwB,EACxB,SAAiB;IAEjB,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;IACxC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE7B,MAAM,CAAC,KAAK,CAAC,sBAAsB,EAAE;QACnC,SAAS;QACT,QAAQ;QACR,KAAK,EAAE,GAAG,CAAC,MAAM;KAClB,CAAC,CAAC;IAEH,oBAAoB;IACpB,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO;YACL,KAAK,EAAE,CAAC;YACR,UAAU,EAAE,EAAE;YACd,SAAS,EAAE,EAAE;YACb,MAAM,EAAE,EAAE;SACX,CAAC;IACJ,CAAC;IAED,8BAA8B;IAC9B,oBAAoB,CAAC,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;IAEpE,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;IAEjC,yBAAyB;IACzB,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QAC5B,EAAE,EAAE,QAAQ;QACZ,EAAE,EAAE,oBAAoB,CAAC,EAAE,CAAC;KAC7B,CAAC,CAAC,CAAC;IAEJ,MAAM,cAAc,GAAG,MAAM,wBAAwB,CACnD,GAAG,EAAE,CACH,QAAQ,CAAC,IAAI,CACX,IAAI,eAAe,CAAC;QAClB,YAAY,EAAE;YACZ,CAAC,SAAS,CAAC,EAAE;gBACX,IAAI,EAAE,IAAI;gBACV,cAAc,EAAE,IAAI;aACrB;SACF;KACF,CAAC,CACH,EACH,cAAc,CACf,CAAC;IAEF,MAAM,aAAa,GAAG,cAAc,CAAC,SAAS,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;IAElE,oBAAoB;IACpB,MAAM,WAAW,GAAG,IAAI,GAAG,CACzB,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,IAA+B,CAAC;QAClD,OAAO,IAAI,CAAC,EAAY,CAAC;IAC3B,CAAC,CAAC,CACH,CAAC;IAEF,aAAa;IACb,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAE7D,2BAA2B;IAC3B,MAAM,YAAY,GAAG,eAAe,EAAE,CAAC;IACvC,MAAM,YAAY,GAAG,iBAAiB,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;IAE/D,MAAM,eAAe,GAA2B,EAAE,CAAC;IACnD,MAAM,oBAAoB,GAAa,EAAE,CAAC;IAC1C,MAAM,iBAAiB,GAAqB,EAAE,CAAC;IAE/C,WAAW;IACX,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,YAAY,GAAG,IAAI,CAAC,IAA+B,CAAC;YAC1D,MAAM,EAAE,GAAG,YAAY,CAAC,EAAY,CAAC;YACrC,MAAM,aAAa,GAAI,YAAY,CAAC,YAAyB,IAAI,EAAE,CAAC;YAEpE,sBAAsB;YACtB,MAAM,UAAU,GAAG,mBAAmB,CAAC,gBAAgB,CAAC,YAAY,CAAC,EAAE,SAAS,CAAC,CAAC;YAElF,qCAAqC;YACrC,MAAM,WAAW,GAA4B,kBAAkB,CAAC;gBAC9D,GAAG,UAAU;gBACb,EAAE;aACH,CAAC,CAAC;YAEH,iBAAiB;YACjB,MAAM,gBAAgB,GAAG,qBAAqB,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;YAC1E,MAAM,aAAa,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAElE,gBAAgB;YAChB,MAAM,MAAM,GAAG,oBAAoB,CAAC,EAAE,CAAC,CAAC;YAExC,eAAe,CAAC,IAAI,CAAC;gBACnB,EAAE;gBACF,WAAW;gBACX,aAAa;gBACb,aAAa;gBACb,MAAM;aACP,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,mBAAmB;YACnB,MAAM,YAAY,GAAG,IAAI,CAAC,IAA+B,CAAC;YAC1D,MAAM,QAAQ,GAAI,YAAY,CAAC,EAAa,IAAI,YAAY,CAAC;YAC7D,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,2BAA2B,CAAC;YAC1F,MAAM,SAAS,GAAG,uBAAuB,CAAC,KAAK,CAAC,CAAC;YAEjD,MAAM,CAAC,KAAK,CAAC,qCAAqC,EAAE;gBAClD,SAAS;gBACT,QAAQ,EAAE,QAAQ;gBAClB,KAAK,EAAE,YAAY;gBACnB,SAAS;aACV,CAAC,CAAC;YAEH,iBAAiB;YACjB,oBAAoB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACpC,iBAAiB,CAAC,IAAI,CAAC;gBACrB,EAAE,EAAE,QAAQ;gBACZ,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,YAAY;aACtB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,mCAAmC;IACnC,MAAM,YAAY,GAAG,CAAC,MAA4B,EAAU,EAAE;QAC5D,MAAM,UAAU,GAAG,mBAAmB,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC;QACnF,OAAO,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC;IAClE,CAAC,CAAC;IAEF,SAAS;IACT,MAAM,EAAE,MAAM,EAAE,GAAG,eAAe,CAAC,eAAe,EAAE,YAAY,CAAC,CAAC;IAElE,4BAA4B;IAC5B,MAAM,mBAAmB,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAC5D,yBAAyB,CACvB,SAAS,EACT,QAAQ,EACR,eAAe,CAAC,MAAM,EACtB,MAAM,CAAC,MAAM,EACb,mBAAmB,CACpB,CAAC;IAEF,WAAW;IACX,MAAM,YAAY,GAAG,KAAK,EAAE,KAA6B,EAAmC,EAAE;QAC5F,MAAM,aAAa,GAGd,EAAE,CAAC;QAER,+BAA+B;QAC/B,KAAK,MAAM,MAAM,IAAI,KAAK,EAAE,CAAC;YAC3B,YAAY;YACZ,MAAM,UAAU,GAAG,mBAAmB,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC;YAEnF,aAAa;YACb,aAAa,CAAC,IAAI,CAAC;gBACjB,GAAG,EAAE;oBACH,SAAS,EAAE,SAAS;oBACpB,IAAI,EAAE;wBACJ,EAAE,EAAE,QAAQ;wBACZ,EAAE,EAAE,MAAM,CAAC,MAAM;wBACjB,IAAI,EAAE;4BACJ,GAAG,MAAM,CAAC,WAAW;4BACrB,YAAY,EAAE,MAAM,CAAC,aAAa;yBACnC;qBACF;iBACF;aACF,CAAC,CAAC;YAEH,WAAW;YACX,KAAK,MAAM,QAAQ,IAAI,UAAU,CAAC,QAAQ,EAAE,CAAC;gBAC3C,aAAa,CAAC,IAAI,CAAC;oBACjB,MAAM,EAAE;wBACN,SAAS,EAAE,SAAS;wBACpB,GAAG,EAAE;4BACH,EAAE,EAAE,QAAQ;4BACZ,EAAE,EAAE,QAAQ;yBACb;qBACF;iBACF,CAAC,CAAC;YACL,CAAC;YAED,WAAW;YACX,KAAK,MAAM,QAAQ,IAAI,UAAU,CAAC,KAAK,EAAE,CAAC;gBACxC,aAAa,CAAC,IAAI,CAAC;oBACjB,GAAG,EAAE;wBACH,SAAS,EAAE,SAAS;wBACpB,IAAI,EAAE;4BACJ,EAAE,EAAE,QAAQ;4BACZ,EAAE,EAAE,QAAQ;4BACZ,IAAI,EAAE,EAAE;yBACT;qBACF;iBACF,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,wBAAwB;QACxB,MAAM,wBAAwB,CAC5B,GAAG,EAAE,CACH,QAAQ,CAAC,IAAI,CACX,IAAI,oBAAoB,CAAC;YACvB,aAAa,EAAE,aAAa;SAC7B,CAAC,CACH,EACH,oBAAoB,CACrB,CAAC;QAEF,cAAc;QACd,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IAEF,YAAY;IACZ,MAAM,EACJ,cAAc,EACd,SAAS,EAAE,cAAc,EACzB,MAAM,EAAE,WAAW,GACpB,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAErE,eAAe;IACf,mDAAmD;IACnD,MAAM,UAAU,GAA2B,EAAE,CAAC;IAC9C,MAAM,YAAY,GAA2B,EAAE,CAAC;IAChD,MAAM,SAAS,GAAmC,EAAE,CAAC;IAErD,qBAAqB;IACrB,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC9D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QAClB,IAAI,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YACzB,UAAU,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;QACrB,CAAC;IACH,CAAC;IAED,oBAAoB;IACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QAClB,IAAI,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;YAC7B,YAAY,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;YACrB,SAAS,CAAC,CAAC,CAAC,GAAG;gBACb,EAAE;gBACF,IAAI,EAAE,gBAAgB;gBACtB,OAAO,EAAE,qBAAqB,EAAE,EAAE;aACnC,CAAC;QACJ,CAAC;IACH,CAAC;IAED,iBAAiB;IACjB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QAClB,IAAI,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;YACtC,YAAY,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;YACrB,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;YACzD,IAAI,KAAK,EAAE,CAAC;gBACV,SAAS,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;YACvB,CAAC;QACH,CAAC;IACH,CAAC;IAED,qBAAqB;IACrB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QAClB,IAAI,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;YAChC,YAAY,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;YACrB,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;YACnD,IAAI,KAAK,EAAE,CAAC;gBACV,SAAS,CAAC,CAAC,CAAC,GAAG;oBACb,EAAE,EAAE,KAAK,CAAC,EAAE;oBACZ,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,OAAO,EAAE,KAAK,CAAC,OAAO;iBACvB,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC;IAC7C,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IAEjD,sBAAsB;IACtB,MAAM,kBAAkB,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAE3D,wBAAwB,CACtB,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,GAAG,CAAC,MAAM,EACV,KAAK,EACL,YAAY,CAAC,MAAM,EACnB,kBAAkB,EAClB;QACE,OAAO,EAAE,KAAK;QACd,QAAQ,EAAE,WAAW,CAAC,MAAM;QAC5B,iBAAiB,EAAE,oBAAoB,CAAC,MAAM;QAC9C,oBAAoB,EAAE,cAAc,CAAC,MAAM;KAC5C,CACF,CAAC;IAEF,0BAA0B;IAC1B,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,iBAAiB,CACf,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,GAAG,CAAC,MAAM,EACV,KAAK,EACL,YAAY,CAAC,MAAM,EACnB,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAC3C;YACE,aAAa,EAAE,WAAW,CAAC,MAAM;YACjC,mBAAmB,EAAE,oBAAoB,CAAC,MAAM;YAChD,sBAAsB,EAAE,cAAc,CAAC,MAAM;SAC9C,CACF,CAAC;IACJ,CAAC;IAED,eAAe;IACf,OAAO;QACL,KAAK;QACL,UAAU;QACV,SAAS,EAAE,YAAY;QACvB,MAAM,EAAE,SAAS;KAClB,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,uBAAuB,CAAC,KAAc;IAC7C,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvC,UAAU;QACV,IAAI,MAAM,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtD,OAAO,KAAK,CAAC,IAAI,CAAC;QACpB,CAAC;QACD,cAAc;QACd,IAAI,MAAM,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtD,OAAO,KAAK,CAAC,IAAI,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,kBAAkB,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { UpdateOneParams, UpdateOneResult } from '../types.js';
2
+ /**
3
+ * updateOne 操作を実行する
4
+ *
5
+ * 処理フロー:
6
+ * 1. GetItemで既存レコードを取得
7
+ * 2. JSON Merge Patchを適用
8
+ * 3. updatedAtタイムスタンプを更新
9
+ * 4. 新しいシャドーSKを生成
10
+ * 5. 旧シャドーと新シャドーの差分を計算
11
+ * 6. TransactWriteItemsでメインレコード更新 + 旧シャドー削除 + 新シャドー追加
12
+ *
13
+ * @param resource - リソース名
14
+ * @param params - updateOneパラメータ
15
+ * @param requestId - リクエストID
16
+ * @returns 更新されたレコード
17
+ * @throws {ItemNotFoundError} レコードが存在しない場合
18
+ */
19
+ export declare function handleUpdateOne(resource: string, params: UpdateOneParams, requestId: string): Promise<UpdateOneResult>;
20
+ //# sourceMappingURL=updateOne.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"updateOne.d.ts","sourceRoot":"","sources":["../../../src/server/operations/updateOne.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAuDpE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,eAAe,EACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,eAAe,CAAC,CA8H1B"}
@@ -0,0 +1,159 @@
1
+ /**
2
+ * updateOne 操作
3
+ * 単一レコードを更新する(JSON Merge Patch形式)
4
+ *
5
+ * 要件: 4.2, 4.4, 5.2, 5.3
6
+ */
7
+ import { GetCommand, TransactWriteCommand } from '@aws-sdk/lib-dynamodb';
8
+ import { ItemNotFoundError, createLogger, generateShadowRecords, getResourceSchema, getShadowConfig, } from '../../index.js';
9
+ import { calculateShadowDiff, generateMainRecordSK, isDiffEmpty } from '../shadow/index.js';
10
+ import { executeDynamoDBOperation, getDBClient, getTableName, removeShadowKeys, } from '../utils/dynamodb.js';
11
+ import { addUpdateTimestamp } from '../utils/timestamps.js';
12
+ const logger = createLogger({ service: 'records-lambda' });
13
+ /**
14
+ * JSON Merge Patch (RFC 7396) を適用する
15
+ *
16
+ * ルール:
17
+ * - null値はフィールド削除を意味する
18
+ * - 配列は完全置換される
19
+ * - オブジェクトは再帰的にマージされる
20
+ *
21
+ * @param target - 対象オブジェクト
22
+ * @param patch - パッチオブジェクト
23
+ * @returns マージされたオブジェクト
24
+ */
25
+ function applyJsonMergePatch(target, patch) {
26
+ const result = { ...target };
27
+ for (const [key, value] of Object.entries(patch)) {
28
+ if (value === null) {
29
+ // null値はフィールド削除
30
+ delete result[key];
31
+ }
32
+ else if (typeof value === 'object' &&
33
+ !Array.isArray(value) &&
34
+ value !== null &&
35
+ typeof result[key] === 'object' &&
36
+ !Array.isArray(result[key]) &&
37
+ result[key] !== null) {
38
+ // オブジェクトは再帰的にマージ
39
+ result[key] = applyJsonMergePatch(result[key], value);
40
+ }
41
+ else {
42
+ // その他(配列、プリミティブ)は完全置換
43
+ result[key] = value;
44
+ }
45
+ }
46
+ return result;
47
+ }
48
+ /**
49
+ * updateOne 操作を実行する
50
+ *
51
+ * 処理フロー:
52
+ * 1. GetItemで既存レコードを取得
53
+ * 2. JSON Merge Patchを適用
54
+ * 3. updatedAtタイムスタンプを更新
55
+ * 4. 新しいシャドーSKを生成
56
+ * 5. 旧シャドーと新シャドーの差分を計算
57
+ * 6. TransactWriteItemsでメインレコード更新 + 旧シャドー削除 + 新シャドー追加
58
+ *
59
+ * @param resource - リソース名
60
+ * @param params - updateOneパラメータ
61
+ * @param requestId - リクエストID
62
+ * @returns 更新されたレコード
63
+ * @throws {ItemNotFoundError} レコードが存在しない場合
64
+ */
65
+ export async function handleUpdateOne(resource, params, requestId) {
66
+ const { id, data: patchData } = params;
67
+ logger.debug('Executing updateOne', {
68
+ requestId,
69
+ resource,
70
+ id,
71
+ });
72
+ const dbClient = getDBClient();
73
+ const tableName = getTableName();
74
+ // メインレコードのSKを生成
75
+ const mainSK = generateMainRecordSK(id);
76
+ // 既存レコードを取得
77
+ const getResult = await executeDynamoDBOperation(() => dbClient.send(new GetCommand({
78
+ TableName: tableName,
79
+ Key: {
80
+ PK: resource,
81
+ SK: mainSK,
82
+ },
83
+ ConsistentRead: true,
84
+ })), 'GetItem');
85
+ if (!getResult.Item) {
86
+ throw new ItemNotFoundError(`Record not found: ${id}`, { resource, id });
87
+ }
88
+ const existingData = getResult.Item.data;
89
+ const oldShadowKeys = existingData.__shadowKeys || [];
90
+ // JSON Merge Patchを適用
91
+ const mergedData = applyJsonMergePatch(removeShadowKeys(existingData), patchData);
92
+ // updatedAt を更新(タイムスタンプフィールド名は動的に取得)
93
+ const updatedData = addUpdateTimestamp({
94
+ ...mergedData,
95
+ id, // IDは変更不可
96
+ });
97
+ // シャドー設定を取得(環境変数からキャッシュ付き)
98
+ const shadowConfig = getShadowConfig();
99
+ const shadowSchema = getResourceSchema(shadowConfig, resource);
100
+ // 新しいシャドーレコードを生成
101
+ const newShadowRecords = generateShadowRecords(updatedData, shadowSchema);
102
+ const newShadowKeys = newShadowRecords.map((shadow) => shadow.SK);
103
+ // シャドー差分を計算
104
+ const shadowDiff = calculateShadowDiff(oldShadowKeys, newShadowKeys);
105
+ // TransactWriteItemsで一括更新
106
+ const transactItems = [];
107
+ // メインレコードを更新
108
+ transactItems.push({
109
+ Put: {
110
+ TableName: tableName,
111
+ Item: {
112
+ PK: resource,
113
+ SK: mainSK,
114
+ data: {
115
+ ...updatedData,
116
+ __shadowKeys: newShadowKeys,
117
+ },
118
+ },
119
+ },
120
+ });
121
+ // 旧シャドーを削除
122
+ for (const shadowSK of shadowDiff.toDelete) {
123
+ transactItems.push({
124
+ Delete: {
125
+ TableName: tableName,
126
+ Key: {
127
+ PK: resource,
128
+ SK: shadowSK,
129
+ },
130
+ },
131
+ });
132
+ }
133
+ // 新シャドーを追加
134
+ for (const shadowRecord of newShadowRecords) {
135
+ if (shadowDiff.toAdd.includes(shadowRecord.SK)) {
136
+ transactItems.push({
137
+ Put: {
138
+ TableName: tableName,
139
+ Item: shadowRecord,
140
+ },
141
+ });
142
+ }
143
+ }
144
+ // トランザクション実行
145
+ await executeDynamoDBOperation(() => dbClient.send(new TransactWriteCommand({
146
+ TransactItems: transactItems,
147
+ })), 'TransactWriteItems');
148
+ logger.info('updateOne succeeded', {
149
+ requestId,
150
+ resource,
151
+ id,
152
+ shadowDiffEmpty: isDiffEmpty(shadowDiff),
153
+ shadowsDeleted: shadowDiff.toDelete.length,
154
+ shadowsAdded: shadowDiff.toAdd.length,
155
+ });
156
+ // __shadowKeysを除外してレスポンスを返す
157
+ return removeShadowKeys(updatedData);
158
+ }
159
+ //# sourceMappingURL=updateOne.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"updateOne.js","sourceRoot":"","sources":["../../../src/server/operations/updateOne.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * クエリ変換ユーティリティ
3
+ *
4
+ * MongoDB風のFilterをDynamoDB形式に変換します。
5
+ *
6
+ * 要件: 8.5
7
+ */
8
+ import type { Filter } from '../../types.js';
9
+ /**
10
+ * DynamoDB比較演算子
11
+ */
12
+ export type DynamoComparisonOperator = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'exists' | 'begins_with' | 'contains';
13
+ /**
14
+ * DynamoDB条件
15
+ */
16
+ export interface DynamoCondition {
17
+ /** フィールド名 */
18
+ field: string;
19
+ /** 比較演算子 */
20
+ operator: DynamoComparisonOperator;
21
+ /** 比較値 */
22
+ value: unknown;
23
+ }
24
+ /**
25
+ * DynamoDBクエリ
26
+ */
27
+ export interface DynamoQuery {
28
+ /** パーティションキー(リソース名) */
29
+ pk: string;
30
+ /** 条件の配列 */
31
+ conditions: DynamoCondition[];
32
+ /** 論理演算子(デフォルト: AND) */
33
+ logicalOperator?: 'AND' | 'OR';
34
+ }
35
+ /**
36
+ * FilterをDynamoDB形式に変換する
37
+ *
38
+ * MongoDB風のFilter構文をDynamoDBのFilterExpression用の条件配列に変換します。
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * // 単純な等価条件
43
+ * const filter: Filter<Product> = { status: 'active' };
44
+ * const query = convertFilterToDynamo('products', filter);
45
+ * // => { pk: 'products', conditions: [{ field: 'status', operator: 'eq', value: 'active' }] }
46
+ *
47
+ * // 演算子を使用した条件
48
+ * const filter: Filter<Product> = {
49
+ * price: { gte: 1000, lte: 5000 },
50
+ * status: { in: ['active', 'pending'] }
51
+ * };
52
+ * const query = convertFilterToDynamo('products', filter);
53
+ * // => {
54
+ * // pk: 'products',
55
+ * // conditions: [
56
+ * // { field: 'price', operator: 'gte', value: 1000 },
57
+ * // { field: 'price', operator: 'lte', value: 5000 },
58
+ * // { field: 'status', operator: 'in', value: ['active', 'pending'] }
59
+ * // ]
60
+ * // }
61
+ *
62
+ * // OR条件
63
+ * const filter: Filter<Product> = {
64
+ * or: [
65
+ * { status: 'active' },
66
+ * { priority: { gte: 5 } }
67
+ * ]
68
+ * };
69
+ * const query = convertFilterToDynamo('products', filter);
70
+ * // => {
71
+ * // pk: 'products',
72
+ * // conditions: [
73
+ * // { field: 'status', operator: 'eq', value: 'active' },
74
+ * // { field: 'priority', operator: 'gte', value: 5 }
75
+ * // ],
76
+ * // logicalOperator: 'OR'
77
+ * // }
78
+ * ```
79
+ *
80
+ * @param collection - コレクション名(リソース名)
81
+ * @param filter - MongoDB風のフィルタ条件
82
+ * @returns DynamoDBクエリ
83
+ */
84
+ export declare function convertFilterToDynamo<T = any>(collection: string, filter: Filter<T>): DynamoQuery;
85
+ //# sourceMappingURL=converter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"converter.d.ts","sourceRoot":"","sources":["../../../src/server/query/converter.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,MAAM,EAAmB,MAAM,gBAAgB,CAAC;AAE9D;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAChC,IAAI,GACJ,IAAI,GACJ,IAAI,GACJ,KAAK,GACL,IAAI,GACJ,KAAK,GACL,IAAI,GACJ,KAAK,GACL,QAAQ,GACR,aAAa,GACb,UAAU,CAAC;AAEf;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,aAAa;IACb,KAAK,EAAE,MAAM,CAAC;IACd,YAAY;IACZ,QAAQ,EAAE,wBAAwB,CAAC;IACnC,UAAU;IACV,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,uBAAuB;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,YAAY;IACZ,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,wBAAwB;IACxB,eAAe,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,GAAG,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,WAAW,CAkEjG"}