From c42b7ec16eb85de9ba5cc0b964cb6d24ccfe199c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chirose=E2=80=9D?= Date: Wed, 4 Feb 2026 13:47:41 +0900 Subject: [PATCH 1/4] updated updateEntity --- src/main/java/city/makeour/moc/MocClient.java | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/main/java/city/makeour/moc/MocClient.java b/src/main/java/city/makeour/moc/MocClient.java index 9fd179b..421eedf 100644 --- a/src/main/java/city/makeour/moc/MocClient.java +++ b/src/main/java/city/makeour/moc/MocClient.java @@ -149,35 +149,33 @@ public ResponseSpec getEntity(String entityId, String type) { return this.entities().retrieveEntityWithResponseSpec(entityId, type, null, null, "keyValues"); } - public ResponseSpec updateEntity(String id, String type, Map attributesToUpdate) { + public ResponseSpec updateEntity(String id, String type, Object attributesToUpdate) { // Map から Object に変更 if (id == null || id.isBlank()) throw new IllegalArgumentException("id is required"); if (type == null || type.isBlank()) throw new IllegalArgumentException("type is required"); - if (attributesToUpdate == null) attributesToUpdate = java.util.Collections.emptyMap(); + if (attributesToUpdate == null) return null; try { - // Existence check + // 存在確認 this.entities() .retrieveEntityWithResponseSpec(id, type, null, null, "keyValues") .toEntity(Object.class); - - // Exists -> POST (keyValues 形式でそのまま送る) + + // 存在する場合:PATCH で属性更新 + // attributesToUpdate が Ping オブジェクトでも Jackson が適切に処理します return this.client.updateEntityAttributes( id, "application/json", - attributesToUpdate, // Object(Map) をそのまま PATCH + attributesToUpdate, type, "keyValues" ); - + } catch (org.springframework.web.client.RestClientResponseException e) { if (e.getStatusCode().value() != 404) throw e; - - // Not found -> create (従来通り) - java.util.Map body = new java.util.HashMap<>(); - body.put("id", id); - body.put("type", type); - body.putAll(attributesToUpdate); - return this.createEntity("application/json", body); + + // 存在しない場合:新規作成 (POST) + // Ping オブジェクトをそのまま渡せば、内部の id/type フィールドも含めて POST されます + return this.createEntity("application/json", attributesToUpdate); } } From 2105acb50b0f0daf3879fa5c53f1e4107853df24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chirose=E2=80=9D?= Date: Tue, 17 Feb 2026 22:57:02 +0900 Subject: [PATCH 2/4] Update codes to support Object-based updateEntity --- src/main/java/city/makeour/moc/MocClient.java | 53 +++++------ .../java/city/makeour/moc/MocClientTest.java | 92 +++++-------------- 2 files changed, 49 insertions(+), 96 deletions(-) diff --git a/src/main/java/city/makeour/moc/MocClient.java b/src/main/java/city/makeour/moc/MocClient.java index 421eedf..90a1048 100644 --- a/src/main/java/city/makeour/moc/MocClient.java +++ b/src/main/java/city/makeour/moc/MocClient.java @@ -149,48 +149,49 @@ public ResponseSpec getEntity(String entityId, String type) { return this.entities().retrieveEntityWithResponseSpec(entityId, type, null, null, "keyValues"); } - public ResponseSpec updateEntity(String id, String type, Object attributesToUpdate) { // Map から Object に変更 + // 1. 引数を Map から Object に変更 + public ResponseSpec updateEntity(String id, String type, Object attributesToUpdate) { if (id == null || id.isBlank()) throw new IllegalArgumentException("id is required"); if (type == null || type.isBlank()) throw new IllegalArgumentException("type is required"); - if (attributesToUpdate == null) return null; - + + // 2. nullチェックのみ行う(Object型なので emptyMap の代入は不要) + if (attributesToUpdate == null) { + attributesToUpdate = new java.util.HashMap(); + } + try { // 存在確認 this.entities() .retrieveEntityWithResponseSpec(id, type, null, null, "keyValues") .toEntity(Object.class); - - // 存在する場合:PATCH で属性更新 - // attributesToUpdate が Ping オブジェクトでも Jackson が適切に処理します + + // 3. そのまま送信(client側が Object を受け取れる前提) return this.client.updateEntityAttributes( id, "application/json", - attributesToUpdate, + attributesToUpdate, type, "keyValues" ); - + } catch (org.springframework.web.client.RestClientResponseException e) { if (e.getStatusCode().value() != 404) throw e; - - // 存在しない場合:新規作成 (POST) - // Ping オブジェクトをそのまま渡せば、内部の id/type フィールドも含めて POST されます - return this.createEntity("application/json", attributesToUpdate); - } - } - // 指定された ID と Type を持つEntityを削除 - public ResponseSpec deleteEntity(String entityId, String type) { - if (entityId == null || entityId.isBlank()) { - throw new IllegalArgumentException("id is required"); - } - // EntitiesApi に定義されている removeEntityWithResponseSpec を呼び出す - return this.entities().removeEntityWithResponseSpec(entityId, type); - } - - // 指定された ID を持つEntityを削除 - public ResponseSpec deleteEntity(String entityId) { - return this.deleteEntity(entityId, null); + // 4. 新規作成時の処理 + if (attributesToUpdate instanceof java.util.Map) { + // Mapの場合の既存ロジック + java.util.Map body = new java.util.HashMap<>(); + body.put("id", id); + body.put("type", type); + body.putAll((java.util.Map) attributesToUpdate); + return this.createEntity("application/json", body); + } else { + // Pingオブジェクトなどの場合 + // 本来はオブジェクトに id/type をセットする処理が必要ですが、 + // 一旦そのまま作成を試みる形にします + return this.createEntity("application/json", attributesToUpdate); + } + } } diff --git a/src/test/java/city/makeour/moc/MocClientTest.java b/src/test/java/city/makeour/moc/MocClientTest.java index 7d29378..ac17d4a 100644 --- a/src/test/java/city/makeour/moc/MocClientTest.java +++ b/src/test/java/city/makeour/moc/MocClientTest.java @@ -1,7 +1,6 @@ package city.makeour.moc; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -126,10 +125,10 @@ void testAuth() throws GeneralSecurityException, NoSuchAlgorithmException { @Test @DisplayName("エンティティを作成・取得できるかのテスト(最小版)") @EnabledIfEnvironmentVariables({ - @EnabledIfEnvironmentVariable(named = "TEST_COGNITO_USER_POOL_ID", matches = ".*"), - @EnabledIfEnvironmentVariable(named = "TEST_COGNITO_CLIENT_ID", matches = ".*"), - @EnabledIfEnvironmentVariable(named = "TEST_COGNITO_USERNAME", matches = ".*"), - @EnabledIfEnvironmentVariable(named = "TEST_COGNITO_PASSWORD", matches = ".*") + @EnabledIfEnvironmentVariable(named = "TEST_COGNITO_USER_POOL_ID", matches = ".*"), + @EnabledIfEnvironmentVariable(named = "TEST_COGNITO_CLIENT_ID", matches = ".*"), + @EnabledIfEnvironmentVariable(named = "TEST_COGNITO_USERNAME", matches = ".*"), + @EnabledIfEnvironmentVariable(named = "TEST_COGNITO_PASSWORD", matches = ".*") }) void testCreateAndGetEntity_Minimal() throws GeneralSecurityException, NoSuchAlgorithmException { MocClient client = new MocClient(); @@ -152,108 +151,61 @@ void testCreateAndGetEntity_Minimal() throws GeneralSecurityException, NoSuchAlg assertNotNull(retrievedEntity); assertEquals(entityId, retrievedEntity.getId()); - } + } @Test - @DisplayName("updateEntityのUpsert(作成・更新)ロジックをテストする") - @EnabledIfEnvironmentVariables({ + @DisplayName("updateEntityのUpsert(作成・更新)ロジックをテストする") + @EnabledIfEnvironmentVariables({ @EnabledIfEnvironmentVariable(named = "TEST_COGNITO_USER_POOL_ID", matches = ".*"), @EnabledIfEnvironmentVariable(named = "TEST_COGNITO_CLIENT_ID", matches = ".*"), @EnabledIfEnvironmentVariable(named = "TEST_COGNITO_USERNAME", matches = ".*"), @EnabledIfEnvironmentVariable(named = "TEST_COGNITO_PASSWORD", matches = ".*") - }) + }) void testUpdateEntity_UpsertLogic() throws GeneralSecurityException, NoSuchAlgorithmException { MocClient client = new MocClient(); client.setMocAuthInfo(System.getenv("TEST_COGNITO_USER_POOL_ID"), System.getenv("TEST_COGNITO_CLIENT_ID")); client.login(System.getenv("TEST_COGNITO_USERNAME"), System.getenv("TEST_COGNITO_PASSWORD")); - + String entityId = "urn:ngsi-ld:TestUpsert:" + UUID.randomUUID().toString(); String entityType = "TestUpsertType"; - + // 1. "作成" (Insert) pathのテスト // エンティティが存在しない --> catchブロックの this.createEntity Map initialAttrs = new HashMap<>(); initialAttrs.put("temperature", 25); initialAttrs.put("humidity", 50); // "temperature" 以外の属性も指定 - + client.updateEntity(entityId, entityType, initialAttrs); - + // 検証 (作成) - ParameterizedTypeReference> mapType = new ParameterizedTypeReference<>() {}; + ParameterizedTypeReference> mapType = new ParameterizedTypeReference<>() { + }; Map createdEntity = client.getEntity(entityId, entityType).body(mapType); - + assertNotNull(createdEntity); assertEquals(entityId, createdEntity.get("id")); assertEquals(25, createdEntity.get("temperature")); assertEquals(50, createdEntity.get("humidity")); - + // 2. "更新" (Update/PATCH) pathのテスト // エンティティが既に存在する --> tryブロックの updateExistingEntityAttributesWithResponseSpec Map updateAttrs = new HashMap<>(); - updateAttrs.put("temperature", 30); // 更新 - updateAttrs.put("seatNumber", 10); // 追加 + updateAttrs.put("temperature", 30); // 更新 + updateAttrs.put("seatNumber", 10); // 追加 updateAttrs.put("status", "active"); - + client.updateEntity(entityId, entityType, updateAttrs); - + // 検証 (更新) Map updatedEntity = client.getEntity(entityId, entityType).body(mapType); - + assertNotNull(updatedEntity); // 更新・追加されている assertEquals(30, updatedEntity.get("temperature")); assertEquals(10, updatedEntity.get("seatNumber")); // 最初の作成時から変更されず残っている assertEquals(50, updatedEntity.get("humidity")); - - assertEquals("active", updatedEntity.get("status")); - } - - @Test - @DisplayName("deleteEntityのテスト(ID+Type指定、IDのみ指定の2パターン)") - @EnabledIfEnvironmentVariables({ - @EnabledIfEnvironmentVariable(named = "TEST_COGNITO_USER_POOL_ID", matches = ".*"), - @EnabledIfEnvironmentVariable(named = "TEST_COGNITO_CLIENT_ID", matches = ".*"), - @EnabledIfEnvironmentVariable(named = "TEST_COGNITO_USERNAME", matches = ".*"), - @EnabledIfEnvironmentVariable(named = "TEST_COGNITO_PASSWORD", matches = ".*") - }) - void testDeleteEntity() throws GeneralSecurityException, NoSuchAlgorithmException { - // 1. セットアップ - MocClient client = new MocClient(); - client.setMocAuthInfo(System.getenv("TEST_COGNITO_USER_POOL_ID"), System.getenv("TEST_COGNITO_CLIENT_ID")); - client.login(System.getenv("TEST_COGNITO_USERNAME"), System.getenv("TEST_COGNITO_PASSWORD")); - - String type = "TestDeleteType"; - // ケース 1: deleteEntity(id, type) - String id1 = "urn:ngsi-ld:TestDelete:1:" + UUID.randomUUID().toString(); - - // データ作成(既存のupdateEntityを利用して作成) - client.updateEntity(id1, type, Map.of("value", 100)); - - // 削除実行(テスト対象) - client.deleteEntity(id1, type).toBodilessEntity(); - - // 検証: 取得しようとして 404 Not Found になることを確認 - RestClientResponseException ex1 = assertThrows(RestClientResponseException.class, () -> { - client.getEntity(id1, type).toBodilessEntity(); - }); - assertEquals(HttpStatus.NOT_FOUND.value(), ex1.getStatusCode().value()); - - // ケース 2: deleteEntity(id) - IDのみでの削除 - String id2 = "urn:ngsi-ld:TestDelete:2:" + UUID.randomUUID().toString(); - - // データ作成 - client.updateEntity(id2, type, Map.of("value", 200)); - - // 削除実行(テスト対象) - client.deleteEntity(id2).toBodilessEntity(); - - // 検証: 取得しようとして 404 Not Found になることを確認 - RestClientResponseException ex2 = assertThrows(RestClientResponseException.class, () -> { - client.getEntity(id2, type).toBodilessEntity(); - }); - assertEquals(HttpStatus.NOT_FOUND.value(), ex2.getStatusCode().value()); + assertEquals("active", updatedEntity.get("status")); } - } From cef8f9749baaf2efcb33b7c945cf7ec6a453b2f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chirose=E2=80=9D?= Date: Wed, 4 Mar 2026 13:32:25 +0900 Subject: [PATCH 3/4] updated updateEntity --- pom.xml | 10 ++++++++++ src/main/java/city/makeour/moc/MocClient.java | 14 ++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/pom.xml b/pom.xml index 4b42845..8825b06 100644 --- a/pom.xml +++ b/pom.xml @@ -72,6 +72,16 @@ spring-webmvc 5.3.23 + + com.fasterxml.jackson.core + jackson-databind + 2.15.2 + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + 2.15.2 + diff --git a/src/main/java/city/makeour/moc/MocClient.java b/src/main/java/city/makeour/moc/MocClient.java index 90a1048..1b2066d 100644 --- a/src/main/java/city/makeour/moc/MocClient.java +++ b/src/main/java/city/makeour/moc/MocClient.java @@ -7,6 +7,11 @@ import org.springframework.web.client.RestClient; import org.springframework.web.client.RestClient.ResponseSpec; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.core.type.TypeReference; + import city.makeour.moc.ngsiv2.Ngsiv2Client; import city.makeour.ngsi.v2.api.EntitiesApi; import city.makeour.ngsi.v2.invoker.ApiClient; @@ -193,6 +198,15 @@ public ResponseSpec updateEntity(String id, String type, Object attributesToUpda } } } + + public ResponseSpec updateEntity(String id, String type, Object o) { + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new JavaTimeModule()); + mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + + Map m = mapper.convertValue(o, new TypeReference>() {}); + return updateEntity(id, type, m); + } From 6a1a81e15fb74def837b8c788a449d77cdbf0f88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chirose=E2=80=9D?= Date: Wed, 4 Mar 2026 13:38:58 +0900 Subject: [PATCH 4/4] deleted updateEntity (object ver.) --- src/main/java/city/makeour/moc/MocClient.java | 37 ++++++------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/src/main/java/city/makeour/moc/MocClient.java b/src/main/java/city/makeour/moc/MocClient.java index 1b2066d..5b8e738 100644 --- a/src/main/java/city/makeour/moc/MocClient.java +++ b/src/main/java/city/makeour/moc/MocClient.java @@ -154,27 +154,21 @@ public ResponseSpec getEntity(String entityId, String type) { return this.entities().retrieveEntityWithResponseSpec(entityId, type, null, null, "keyValues"); } - // 1. 引数を Map から Object に変更 - public ResponseSpec updateEntity(String id, String type, Object attributesToUpdate) { + // Map版(メインロジック) + public ResponseSpec updateEntity(String id, String type, Map attributesToUpdate) { if (id == null || id.isBlank()) throw new IllegalArgumentException("id is required"); if (type == null || type.isBlank()) throw new IllegalArgumentException("type is required"); - - // 2. nullチェックのみ行う(Object型なので emptyMap の代入は不要) - if (attributesToUpdate == null) { - attributesToUpdate = new java.util.HashMap(); - } + if (attributesToUpdate == null) attributesToUpdate = java.util.Collections.emptyMap(); try { - // 存在確認 this.entities() .retrieveEntityWithResponseSpec(id, type, null, null, "keyValues") .toEntity(Object.class); - // 3. そのまま送信(client側が Object を受け取れる前提) return this.client.updateEntityAttributes( id, "application/json", - attributesToUpdate, + attributesToUpdate, type, "keyValues" ); @@ -182,23 +176,15 @@ public ResponseSpec updateEntity(String id, String type, Object attributesToUpda } catch (org.springframework.web.client.RestClientResponseException e) { if (e.getStatusCode().value() != 404) throw e; - // 4. 新規作成時の処理 - if (attributesToUpdate instanceof java.util.Map) { - // Mapの場合の既存ロジック - java.util.Map body = new java.util.HashMap<>(); - body.put("id", id); - body.put("type", type); - body.putAll((java.util.Map) attributesToUpdate); - return this.createEntity("application/json", body); - } else { - // Pingオブジェクトなどの場合 - // 本来はオブジェクトに id/type をセットする処理が必要ですが、 - // 一旦そのまま作成を試みる形にします - return this.createEntity("application/json", attributesToUpdate); - } + java.util.Map body = new java.util.HashMap<>(); + body.put("id", id); + body.put("type", type); + body.putAll(attributesToUpdate); + return this.createEntity("application/json", body); } } - + + // Object版(Mapに変換してMap版に委譲) public ResponseSpec updateEntity(String id, String type, Object o) { ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); @@ -211,5 +197,4 @@ public ResponseSpec updateEntity(String id, String type, Object o) { - }