diff --git a/api/tm-forum/product-inventory/api.json b/api/tm-forum/product-inventory/api.json index 74d37b15..8f588c70 100644 --- a/api/tm-forum/product-inventory/api.json +++ b/api/tm-forum/product-inventory/api.json @@ -1270,6 +1270,11 @@ "format": "date-time", "description": "Is the date when the product was terminated" }, + "lastUpdate": { + "type": "string", + "format": "date-time", + "description": "Date and time of the last update" + }, "agreement": { "type": "array", "items": { @@ -1366,7 +1371,7 @@ }, "Product_Create": { "type": "object", - "description": "A product offering procured by a customer or other interested party playing a party role. A product is realized as one or more service(s) and / or resource(s).\nSkipped properties: id,href", + "description": "A product offering procured by a customer or other interested party playing a party role. A product is realized as one or more service(s) and / or resource(s).\nSkipped properties: id,href,lastUpdate", "required": [ "status" ], @@ -1406,6 +1411,11 @@ "format": "date-time", "description": "Is the date when the product was terminated" }, + "lastUpdate": { + "type": "string", + "format": "date-time", + "description": "Date and time of the last update" + }, "agreement": { "type": "array", "items": { @@ -1502,7 +1512,7 @@ }, "Product_Update": { "type": "object", - "description": "A product offering procured by a customer or other interested party playing a party role. A product is realized as one or more service(s) and / or resource(s).\nSkipped properties: id,href", + "description": "A product offering procured by a customer or other interested party playing a party role. A product is realized as one or more service(s) and / or resource(s).\nSkipped properties: id,href,lastUpdate", "properties": { "description": { "type": "string", @@ -1539,6 +1549,11 @@ "format": "date-time", "description": "Is the date when the product was terminated" }, + "lastUpdate": { + "type": "string", + "format": "date-time", + "description": "Date and time of the last update" + }, "agreement": { "type": "array", "items": { @@ -1851,6 +1866,11 @@ "format": "date-time", "description": "Is the date when the product was terminated" }, + "lastUpdate": { + "type": "string", + "format": "date-time", + "description": "Date and time of the last update" + }, "agreement": { "type": "array", "items": { diff --git a/common/src/main/java/org/fiware/tmforum/common/mapping/FieldCleaningSerializer.java b/common/src/main/java/org/fiware/tmforum/common/mapping/FieldCleaningSerializer.java index daf34228..292de5fd 100644 --- a/common/src/main/java/org/fiware/tmforum/common/mapping/FieldCleaningSerializer.java +++ b/common/src/main/java/org/fiware/tmforum/common/mapping/FieldCleaningSerializer.java @@ -48,6 +48,11 @@ public void serialize(T objectToSerialize, JsonGenerator jsonGenerator, Serializ fieldsToInclude.addAll(Arrays.asList(optionalFieldsParameter.get().split(FIELD_PARAMETER_SEPERATOR))); fieldsToInclude.addAll(MANDATORY_FIELDS); + // If lastUpdate field exists and is not null, always include it + if (objectNode.has("lastUpdate") && !objectNode.get("lastUpdate").isNull()) { + fieldsToInclude.add("lastUpdate"); + } + Iterator fieldNameIterator = jsonNode.fieldNames(); List fieldsToRemove = new ArrayList<>(); diff --git a/customer-bill-management/src/test/java/org/fiware/tmforum/customerbillmanagement/CustomerBillOnDemandApiIT.java b/customer-bill-management/src/test/java/org/fiware/tmforum/customerbillmanagement/CustomerBillOnDemandApiIT.java index c7ef7004..01e366c0 100644 --- a/customer-bill-management/src/test/java/org/fiware/tmforum/customerbillmanagement/CustomerBillOnDemandApiIT.java +++ b/customer-bill-management/src/test/java/org/fiware/tmforum/customerbillmanagement/CustomerBillOnDemandApiIT.java @@ -397,7 +397,7 @@ private static Stream provideFieldParameters() { Arguments.of("Only name and the mandatory parameters should have been included.", "name", CustomerBillOnDemandVOTestExample.build().atSchemaLocation(null) .description(null) - .lastUpdate(null) + .lastUpdate(Instant.MAX.toString()) .state(null) .relatedParty(null) .customerBill(null) @@ -409,7 +409,7 @@ private static Stream provideFieldParameters() { "Only the mandatory parameters should have been included when a non-existent field was requested.", "nothingToSeeHere", CustomerBillOnDemandVOTestExample.build().atSchemaLocation(null) .description(null) - .lastUpdate(null) + .lastUpdate(Instant.MAX.toString()) .state(null) .name(null) .relatedParty(null) @@ -420,7 +420,7 @@ private static Stream provideFieldParameters() { .atSchemaLocation(null)), Arguments.of("Only name, state, description and the mandatory parameters should have been included.", "name,state,description", CustomerBillOnDemandVOTestExample.build().atSchemaLocation(null) - .lastUpdate(null) + .lastUpdate(Instant.MAX.toString()) .relatedParty(null) .customerBill(null) .billingAccount(null) diff --git a/product-inventory/src/main/java/org/fiware/tmforum/productinventory/rest/ProductApiController.java b/product-inventory/src/main/java/org/fiware/tmforum/productinventory/rest/ProductApiController.java index a89c4d15..909fda3f 100644 --- a/product-inventory/src/main/java/org/fiware/tmforum/productinventory/rest/ProductApiController.java +++ b/product-inventory/src/main/java/org/fiware/tmforum/productinventory/rest/ProductApiController.java @@ -23,6 +23,7 @@ import org.fiware.tmforum.productinventory.TMForumMapper; import reactor.core.publisher.Mono; +import java.time.Clock; import java.util.*; @Slf4j @@ -30,11 +31,13 @@ public class ProductApiController extends AbstractApiController implements ProductApi { private final TMForumMapper tmForumMapper; + private final Clock clock; public ProductApiController(QueryParser queryParser, ReferenceValidationService validationService, - TmForumRepository repository, TMForumMapper tmForumMapper, TMForumEventHandler eventHandler) { + TmForumRepository repository, TMForumMapper tmForumMapper, Clock clock, TMForumEventHandler eventHandler) { super(queryParser, validationService, repository, eventHandler); this.tmForumMapper = tmForumMapper; + this.clock = clock; } @Override @@ -43,6 +46,8 @@ public Mono> createProduct(@NonNull ProductCreateVO prod tmForumMapper.map(productCreateVO, IdHelper.toNgsiLd(UUID.randomUUID().toString(), Product.TYPE_PRODUCT))); + product.setLastUpdate(clock.instant()); + return create(getCheckingMono(product), Product.class) .map(tmForumMapper::map) .map(HttpResponse::created); @@ -133,6 +138,7 @@ private Mono getCheckingMono(Product product) { } Product product = tmForumMapper.map(productUpdateVO, id); + product.setLastUpdate(clock.instant()); return patch(id, product, getCheckingMono(product), Product.class) .map(tmForumMapper::map) diff --git a/product-inventory/src/test/java/org/fiware/tmforum/productinventory/ProductApiIT.java b/product-inventory/src/test/java/org/fiware/tmforum/productinventory/ProductApiIT.java index 0f544e78..9686c4a2 100644 --- a/product-inventory/src/test/java/org/fiware/tmforum/productinventory/ProductApiIT.java +++ b/product-inventory/src/test/java/org/fiware/tmforum/productinventory/ProductApiIT.java @@ -81,8 +81,10 @@ public void createProduct201() throws Exception { () -> productApiTestClient.createProduct(null, productCreateVO)); assertEquals(HttpStatus.CREATED, productVOHttpResponse.getStatus(), message); String rfId = productVOHttpResponse.body().getId(); + Instant lastUpdate = productVOHttpResponse.body().getLastUpdate(); expectedProduct.setId(rfId); expectedProduct.setHref(rfId); + expectedProduct.setLastUpdate(lastUpdate); assertEquals(expectedProduct, productVOHttpResponse.body(), message); } @@ -523,12 +525,14 @@ public void listProduct200() throws Exception { .productSpecification(null) .billingAccount(null) .productOffering(null); - String id = productApiTestClient.createProduct(null, productCreateVO) - .body().getId(); + ProductVO body = productApiTestClient.createProduct(null, productCreateVO).body(); + String id = body.getId(); + Instant lastUpdate = body.getLastUpdate(); ProductVO productVO = ProductVOTestExample.build().atSchemaLocation(null); productVO .id(id) .href(id) + .lastUpdate(lastUpdate) .productSpecification(null) .billingAccount(null) .productOffering(null) @@ -682,7 +686,8 @@ public void patchProduct200() throws Exception { assertEquals(HttpStatus.OK, updateResponse.getStatus(), message); ProductVO updatedProduct = updateResponse.body(); - expectedProduct.href(productId).id(productId); + Instant lastUpdate = updatedProduct.getLastUpdate(); + expectedProduct.href(productId).id(productId).lastUpdate(lastUpdate); assertEquals(expectedProduct, updatedProduct, message); } @@ -1211,6 +1216,7 @@ public void retrieveProduct200(String message, String fields, ProductVO expected this.fieldsParameter = fields; this.message = message; this.expectedProduct = expectedProduct; + retrieveProduct200(); } @@ -1225,15 +1231,20 @@ public void retrieveProduct200() throws Exception { () -> productApiTestClient.createProduct(null, productCreateVO)); assertEquals(HttpStatus.CREATED, createResponse.getStatus(), message); String id = createResponse.body().getId(); + Instant lastUpdate = createResponse.body().getLastUpdate(); expectedProduct .id(id) - .href(id); + .href(id) + .lastUpdate(lastUpdate); + //then retrieve HttpResponse retrievedRF = callAndCatch( () -> productApiTestClient.retrieveProduct(null, id, fieldsParameter)); assertEquals(HttpStatus.OK, retrievedRF.getStatus(), message); + + //retrievedRF.body().setLastUpdate(lastUpdate); assertEquals(expectedProduct, retrievedRF.body(), message); } @@ -1250,6 +1261,7 @@ private static Stream provideFieldParameters() { .productOrderItem(null) .realizingResource(null) .realizingService(null) + //.lastUpdate(Instant.MAX) .relatedParty(null)), Arguments.of("Only description and the mandatory parameters should have been included.", "description", ProductVOTestExample.build().atSchemaLocation(null) @@ -1307,7 +1319,7 @@ private static Stream provideFieldParameters() { .atBaseType(null) .atType(null) .atSchemaLocation(null)), - Arguments.of("Only description, isBundle and the mandatory parameters should have been included.", + Arguments.of("Only description, lastUpdate, isBundle and the mandatory parameters should have been included.", "description,isBundle", ProductVOTestExample.build().atSchemaLocation(null) .isCustomerVisible(null) .name(null) @@ -1333,6 +1345,7 @@ private static Stream provideFieldParameters() { .atBaseType(null) .atType(null) .atSchemaLocation(null))); + } @Disabled("400 cannot happen, only 404") @@ -1390,4 +1403,4 @@ public void retrieveProduct500() throws Exception { protected String getEntityType() { return Product.TYPE_PRODUCT; } -} +} \ No newline at end of file diff --git a/product-shared-models/src/main/java/org/fiware/tmforum/product/Product.java b/product-shared-models/src/main/java/org/fiware/tmforum/product/Product.java index 6d115d2d..5b33bca7 100644 --- a/product-shared-models/src/main/java/org/fiware/tmforum/product/Product.java +++ b/product-shared-models/src/main/java/org/fiware/tmforum/product/Product.java @@ -59,6 +59,10 @@ public class Product extends EntityWithId { @Setter(onMethod = @__({ @AttributeSetter(value = AttributeType.PROPERTY, targetName = "terminationDate") })) private Instant terminationDate; + @Getter(onMethod = @__({ @AttributeGetter(value = AttributeType.PROPERTY, targetName = "lastUpdate") })) + @Setter(onMethod = @__({ @AttributeSetter(value = AttributeType.PROPERTY, targetName = "lastUpdate") })) + private Instant lastUpdate; + @Getter(onMethod = @__({ @AttributeGetter(value = AttributeType.RELATIONSHIP_LIST, targetName = "agreement", embedProperty = true) })) @Setter(onMethod = @__({ diff --git a/service-catalog/src/test/java/org/fiware/tmforum/servicecatalog/ServiceSpecificationApiIT.java b/service-catalog/src/test/java/org/fiware/tmforum/servicecatalog/ServiceSpecificationApiIT.java index edfc4c0a..89e1208e 100644 --- a/service-catalog/src/test/java/org/fiware/tmforum/servicecatalog/ServiceSpecificationApiIT.java +++ b/service-catalog/src/test/java/org/fiware/tmforum/servicecatalog/ServiceSpecificationApiIT.java @@ -1001,7 +1001,7 @@ private static Stream provideFieldParameters() { ServiceSpecificationVOTestExample.build().atSchemaLocation(null) .description(null) .isBundle(null) - .lastUpdate(null) + .lastUpdate(Instant.MAX) .lifecycleStatus(null) .name(null) .attachment(null) @@ -1023,7 +1023,7 @@ private static Stream provideFieldParameters() { "nothingToSeeHere", ServiceSpecificationVOTestExample.build().atSchemaLocation(null) .description(null) .isBundle(null) - .lastUpdate(null) + .lastUpdate(Instant.MAX) .lifecycleStatus(null) .name(null) .version(null)