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 9fd179b..5b8e738 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; @@ -149,22 +154,21 @@ public ResponseSpec getEntity(String entityId, String type) { return this.entities().retrieveEntityWithResponseSpec(entityId, type, null, null, "keyValues"); } + // 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"); if (attributesToUpdate == null) attributesToUpdate = java.util.Collections.emptyMap(); - + try { - // Existence check this.entities() .retrieveEntityWithResponseSpec(id, type, null, null, "keyValues") .toEntity(Object.class); - // Exists -> POST (keyValues 形式でそのまま送る) return this.client.updateEntityAttributes( id, "application/json", - attributesToUpdate, // Object(Map) をそのまま PATCH + attributesToUpdate, type, "keyValues" ); @@ -172,7 +176,6 @@ public ResponseSpec updateEntity(String id, String type, Map att } 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); @@ -181,22 +184,17 @@ public ResponseSpec updateEntity(String id, String type, Map att } } - // 指定された 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); + // Object版(Mapに変換してMap版に委譲) + 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); } - } 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")); } - }