Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading