Skip to content

Commit

Permalink
Merge pull request #578 from FraunhoferISST/chore/erpadapterrequestty…
Browse files Browse the repository at this point in the history
…pevalidation

chore: cleanup erpadapterrequest
  • Loading branch information
tom-rm-meyer-ISST authored Sep 20, 2024
2 parents b44e653 + 8635810 commit 2e57489
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,14 @@ public class ErpAdapterConfiguration {
@Value("${puris.erpadapter.url}")
private String erpAdapterUrl;

/**
* The URL under which we expect responses from
* the ERP adapter
*/
@Value("${puris.baseurl}" + "${server.servlet.context-path}" + "/erp-adapter")
private String erpResponseUrl;

@Value("${puris.baseurl}")
@Getter(AccessLevel.NONE)
private String purisBaseUrl;

@Value("${server.servlet.context-path}")
@Getter(AccessLevel.NONE)
private String contextPath;

/**
* The auth-key used when accessing the ERP adapter's
Expand Down Expand Up @@ -114,4 +116,16 @@ public long getRefreshInterval() {
// translate minutes to milliseconds
return refreshInterval * 60 * 1000;
}

/**
* Provides the URL, under which we expect to receive a response from the ERP Adapter
*
* @return the response URL
*/
public String getErpResponseUrl() {
String url = purisBaseUrl.endsWith("/") ? purisBaseUrl : purisBaseUrl + "/";
String context = contextPath.startsWith("/") ? contextPath.substring(1) : contextPath;
context = context.endsWith("/") ? context : context + "/";
return url + context + "erp-adapter";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.tractusx.puris.backend.common.edc.domain.model.AssetType;
import org.eclipse.tractusx.puris.backend.erpadapter.domain.model.ErpAdapterRequest;
import org.eclipse.tractusx.puris.backend.erpadapter.logic.service.ErpAdapterTriggerService;
import org.eclipse.tractusx.puris.backend.erpadapter.logic.service.ItemStockErpAdapterService;
import org.eclipse.tractusx.puris.backend.masterdata.logic.service.MaterialPartnerRelationService;
Expand Down Expand Up @@ -79,7 +80,8 @@ public ResponseEntity<?> scheduleErpUpdate(
) {
materialNumber = new String(Base64.getDecoder().decode(materialNumber));
boolean valid = BPNL_PATTERN.matcher(bpnl).matches()
&& NON_EMPTY_NON_VERTICAL_WHITESPACE_PATTERN.matcher(materialNumber).matches();
&& NON_EMPTY_NON_VERTICAL_WHITESPACE_PATTERN.matcher(materialNumber).matches()
&& ErpAdapterRequest.SUPPORTED_TYPES.contains(assetType);
if (valid && mprService.find(bpnl, materialNumber) != null) {
erpAdapterTriggerService.notifyPartnerRequest(bpnl, materialNumber, assetType, directionCharacteristic);
return ResponseEntity.status(201).build();
Expand Down Expand Up @@ -127,6 +129,9 @@ public ResponseEntity<?> putMethod(
}
Dto dto = new Dto(requestId, partnerBpnl, responseType, sammVersion, new Date(responseTimestamp), requestBody);
AssetType assetType = Arrays.stream(AssetType.values()).filter(type -> type.ERP_KEYWORD.equals(responseType)).findFirst().orElse(null);
if (!ErpAdapterRequest.SUPPORTED_TYPES.contains(assetType)) {
return ResponseEntity.badRequest().body("Unsupported Type");
}
int responseCode = 501;
switch (assetType) {
case ITEM_STOCK_SUBMODEL -> responseCode = itemStockErpAdapterService.receiveItemStockUpdate(dto);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,23 @@
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.validation.Constraint;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import jakarta.validation.Payload;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import lombok.*;
import org.eclipse.tractusx.puris.backend.common.edc.domain.model.AssetType;
import org.eclipse.tractusx.puris.backend.common.util.PatternStore;
import org.eclipse.tractusx.puris.backend.stock.logic.dto.itemstocksamm.DirectionCharacteristic;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Date;
import java.util.List;
import java.util.UUID;

@Entity
Expand All @@ -40,6 +50,9 @@
@NoArgsConstructor
@ToString
public class ErpAdapterRequest {

public final static List<AssetType> SUPPORTED_TYPES = List.of(AssetType.ITEM_STOCK_SUBMODEL);

@GeneratedValue
@Id
private UUID id;
Expand All @@ -48,9 +61,8 @@ public class ErpAdapterRequest {
@NotNull
private String partnerBpnl;

@Pattern(regexp = PatternStore.NON_EMPTY_NON_VERTICAL_WHITESPACE_STRING)
@NotNull
private String requestType;
@ValidRequestType
private AssetType requestType;

@Pattern(regexp = PatternStore.NON_EMPTY_NON_VERTICAL_WHITESPACE_STRING)
@NotNull
Expand All @@ -68,4 +80,24 @@ public class ErpAdapterRequest {
private String ownMaterialNumber;

private DirectionCharacteristic directionCharacteristic;

// AssetType validation helpers:
@Constraint(validatedBy = RequestTypeValidator.class)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
private @interface ValidRequestType {
String message() default
"Request Type must be listed in SUPPORTED_TYPES";

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};
}

public static class RequestTypeValidator implements ConstraintValidator<ValidRequestType, AssetType> {
@Override
public boolean isValid(AssetType value, ConstraintValidatorContext context) {
return value != null && SUPPORTED_TYPES.contains(value);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public class ErpAdapterRequestClient {
public Integer sendRequest(ErpAdapterRequest erpAdapterRequest){
HttpUrl.Builder urlBuilder = HttpUrl.parse(erpAdapterConfiguration.getErpAdapterUrl()).newBuilder();
urlBuilder.addQueryParameter("bpnl", erpAdapterRequest.getPartnerBpnl());
urlBuilder.addQueryParameter("request-type", erpAdapterRequest.getRequestType());
urlBuilder.addQueryParameter("request-type", erpAdapterRequest.getRequestType().ERP_KEYWORD);
urlBuilder.addQueryParameter("request-id", erpAdapterRequest.getId().toString());
urlBuilder.addQueryParameter("samm-version", erpAdapterRequest.getSammVersion());
urlBuilder.addQueryParameter("request-timestamp", String.valueOf(erpAdapterRequest.getRequestDate().getTime()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public class ErpAdapterTriggerService {
DirectionCharacteristic directionCharacteristic = dataset.getDirectionCharacteristic().isEmpty() ?
null : DirectionCharacteristic.valueOf(dataset.getDirectionCharacteristic());
request.setDirectionCharacteristic(directionCharacteristic);
request.setRequestType(dataset.getAssetType().ERP_KEYWORD);
request.setRequestType(dataset.getAssetType());
request.setSammVersion(dataset.getAssetType().ERP_SAMMVERSION);
executorService.submit(() -> erpAdapterRequestService.createAndSend(request));

Expand Down Expand Up @@ -156,7 +156,7 @@ public void notifyPartnerRequest(String partnerBpnl, String ownMaterialNumber, A
erpAdapterRequest.setPartnerBpnl(partnerBpnl);
erpAdapterRequest.setOwnMaterialNumber(ownMaterialNumber);
erpAdapterRequest.setDirectionCharacteristic(direction);
erpAdapterRequest.setRequestType(type.ERP_KEYWORD);
erpAdapterRequest.setRequestType(type);
erpAdapterRequest.setSammVersion(type.ERP_SAMMVERSION);
executorService.submit(() -> erpAdapterRequestService.createAndSend(erpAdapterRequest));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import org.assertj.core.api.Assertions;
import org.eclipse.tractusx.puris.backend.common.edc.domain.model.AssetType;
import org.eclipse.tractusx.puris.backend.erpadapter.ErpAdapterConfiguration;
import org.eclipse.tractusx.puris.backend.erpadapter.domain.model.ErpAdapterRequest;
import org.eclipse.tractusx.puris.backend.stock.logic.dto.itemstocksamm.DirectionCharacteristic;
Expand All @@ -36,6 +37,7 @@
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;

import java.io.InputStream;
import java.util.Date;
import java.util.Map;
Expand Down Expand Up @@ -67,7 +69,7 @@ public class ErpAdapterRequestClientTest {

private static final String apiSecret = "my-secret";

private static final String requestType = "itemstock";
private static final AssetType requestType = AssetType.ITEM_STOCK_SUBMODEL;

private static final String sammVersion = "2.0";

Expand Down Expand Up @@ -118,7 +120,7 @@ public void test_should_success() throws Exception {

Assertions.assertThat(parameters.size()).isEqualTo(5);
Assertions.assertThat(parameters.get("bpnl")).isEqualTo(supplierPartnerBpnl);
Assertions.assertThat(parameters.get("request-type")).isEqualTo(requestType);
Assertions.assertThat(parameters.get("request-type")).isEqualTo(requestType.ERP_KEYWORD);
Assertions.assertThat(parameters.get("samm-version")).isEqualTo(sammVersion);
Assertions.assertThat(parameters.get("request-timestamp")).isEqualTo(String.valueOf(erpAdapterRequest.getRequestDate().getTime()));
Assertions.assertThat(parameters.get("request-id")).isEqualTo(uuid.toString());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright (c) 2024 Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V.
* (represented by Fraunhofer ISST)
* Copyright (c) 2024 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/

package org.eclipse.tractusx.puris.backend.erpadapter.logic.service;

import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import org.eclipse.tractusx.puris.backend.common.edc.domain.model.AssetType;
import org.eclipse.tractusx.puris.backend.erpadapter.domain.model.ErpAdapterRequest;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.Date;
import java.util.UUID;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

@ExtendWith(MockitoExtension.class)
public class ErpAdapterRequestValidationTest {

private static final String matNbrCustomer = "MNR-7307-AU340474.002";

private static final String supplierPartnerBpnl = "BPNL1234567890ZZ";

private static Validator validator;

@BeforeEach
void setUp() {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
}

@ParameterizedTest
@EnumSource(AssetType.class)
public void testRequestTypeValidation(AssetType type) {
// given
ErpAdapterRequest erpAdapterRequest = ErpAdapterRequest.builder()
.id(UUID.randomUUID())
.requestDate(new Date())
.partnerBpnl(supplierPartnerBpnl)
.ownMaterialNumber(matNbrCustomer)
.requestType(type)
.sammVersion(type.ERP_SAMMVERSION)
.build();

// when
var violations = validator.validate(erpAdapterRequest);

// then
if (!ErpAdapterRequest.SUPPORTED_TYPES.contains(type)) {
assertEquals(1, violations.size(), "Expected validation errors for unsupported type: " + type);
} else {
assertTrue(violations.isEmpty(), "No validation errors expected for supported type: " + type);
}
}

@Test
public void testRequestTypeValidation() {
// given
ErpAdapterRequest erpAdapterRequest = ErpAdapterRequest.builder()
.id(UUID.randomUUID())
.requestDate(null) // must not be null
.partnerBpnl("wrong-bpnl") // should fail regex check
.ownMaterialNumber("illegal-material-number\n") // should fail regex check
.requestType(AssetType.ITEM_STOCK_SUBMODEL)
.sammVersion(AssetType.ITEM_STOCK_SUBMODEL.ERP_SAMMVERSION)
.build();

// when
var violations = validator.validate(erpAdapterRequest);

// then
assertEquals(3, violations.size());
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.eclipse.tractusx.puris.backend.common.security.SecurityConfig;
import org.eclipse.tractusx.puris.backend.common.security.annotation.WithMockApiKey;
import org.eclipse.tractusx.puris.backend.common.security.logic.ApiKeyAuthenticationProvider;
import org.eclipse.tractusx.puris.backend.common.util.VariablesService;
import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Address;
import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Partner;
import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Site;
Expand Down Expand Up @@ -63,6 +64,9 @@ public class PartnerControllerTest {
@MockBean
private MaterialPartnerRelationService mprService;

@MockBean
private VariablesService variablesService;

private final ModelMapper modelMapper = new ModelMapper();
private final String bpnl = "BPNL2222222222RR";
private final String edcUrl = "https://example.com";
Expand Down

0 comments on commit 2e57489

Please sign in to comment.