Skip to content

Commit

Permalink
Merge pull request #415 from achtzig20/feat/demand-and-capacity-notif…
Browse files Browse the repository at this point in the history
…ications-backend

feat: implemented the backend for demand and capacity notifications 2.0
  • Loading branch information
tom-rm-meyer-ISST authored May 30, 2024
2 parents 8d96e81 + 2df68a9 commit 9595354
Show file tree
Hide file tree
Showing 14 changed files with 934 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,20 +83,23 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.authorizeHttpRequests(
// any request in spring context
(authorizeHttpRequests) -> authorizeHttpRequests
.requestMatchers("/stockView/**",
.requestMatchers(
"/stockView/**",
"/partners/**",
"/materials/**",
"/materialpartnerrelations/**",
"/item-stock/**",
"/production/**",
"/delivery/**",
"/demand/**",
"/demand-and-capacity-notification/**",
"/planned-production/**",
"/material-demand/**",
"/delivery-information/**",
"/edc/**",
"/erp-adapter/**",
"/parttypeinformation/**")
"/parttypeinformation/**"
)
.authenticated()
.requestMatchers("/swagger-ui/**", "/v3/api-docs/**", "/health/**").permitAll()
.dispatcherTypeMatchers(DispatcherType.ERROR).permitAll()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
/*
Copyright (c) 2024 Volkswagen AG
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.demandandcapacitynotification.controller;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

import java.util.stream.Collectors;

import javax.management.openmbean.KeyAlreadyExistsException;

import org.eclipse.tractusx.puris.backend.common.util.PatternStore;
import org.eclipse.tractusx.puris.backend.demandandcapacitynotification.domain.model.OwnDemandAndCapacityNotification;
import org.eclipse.tractusx.puris.backend.demandandcapacitynotification.domain.model.ReportedDemandAndCapacityNotification;
import org.eclipse.tractusx.puris.backend.demandandcapacitynotification.logic.dto.DemandAndCapacityNotificationDto;
import org.eclipse.tractusx.puris.backend.demandandcapacitynotification.logic.service.OwnDemandAndCapacityNotificationService;
import org.eclipse.tractusx.puris.backend.demandandcapacitynotification.logic.service.ReportedDemandAndCapacityNotificationService;
import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Material;
import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Partner;
import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Site;
import org.eclipse.tractusx.puris.backend.masterdata.logic.service.MaterialService;
import org.eclipse.tractusx.puris.backend.masterdata.logic.service.PartnerService;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.Validator;
import jakarta.validation.constraints.Pattern;

@RestController
@RequestMapping("demand-and-capacity-notification")
public class DemandAndCapacityNotificationController {
@Autowired
private OwnDemandAndCapacityNotificationService ownNotificationService;

@Autowired
private ReportedDemandAndCapacityNotificationService reportedNotificationService;

@Autowired
private MaterialService materialService;

@Autowired
private PartnerService partnerService;

@Autowired
private ModelMapper modelMapper;

@Autowired
private Validator validator;

@GetMapping()
@ResponseBody
@Operation(summary = "Get all own notifications", description = "Get all own notifications. Optionally the partner can be filtered by its bpnl.")
public List<DemandAndCapacityNotificationDto> getAllNotifications(Optional<@Pattern(regexp = PatternStore.BPNL_STRING) String> partnerBpnl) {
if (partnerBpnl.isEmpty()) {
return ownNotificationService.findAll().stream().map(this::convertToDto).collect(Collectors.toList());
} else {
return ownNotificationService.findAllByPartnerBpnl(partnerBpnl.get()).stream().map(this::convertToDto).collect(Collectors.toList());
}
}

@PostMapping()
@ResponseBody
@Operation(summary = "Creates a new notification")
@ApiResponses(value = {
@ApiResponse(responseCode = "201", description = "Notification was created."),
@ApiResponse(responseCode = "400", description = "Malformed or invalid request body."),
@ApiResponse(responseCode = "409", description = "Notification already exists. Use PUT instead."),
@ApiResponse(responseCode = "500", description = "Internal Server Error.")
})
@ResponseStatus(HttpStatus.CREATED)
public DemandAndCapacityNotificationDto createNotification(@RequestBody DemandAndCapacityNotificationDto notificationDto) {
if (!validator.validate(notificationDto).isEmpty()) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid Notification.");
}

if (notificationDto.getPartnerBpnl() == null || notificationDto.getPartnerBpnl().isEmpty()) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"Demand Information misses partner identification.");
}

try {
return convertToDto(ownNotificationService.create(convertToEntity(notificationDto)));
} catch (KeyAlreadyExistsException e) {
throw new ResponseStatusException(HttpStatus.CONFLICT, "Notification already exists. Use PUT instead.");
} catch (IllegalArgumentException e) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Notification is invalid.");
} catch (IllegalStateException e) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage());
}
}

@PutMapping()
@Operation(summary = "Updates a notification by its UUID")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Notification was updated."),
@ApiResponse(responseCode = "400", description = "Malformed or invalid request body."),
@ApiResponse(responseCode = "404", description = "Notification does not exist."),
@ApiResponse(responseCode = "500", description = "Internal Server Error.")
})
@ResponseStatus(HttpStatus.OK)
public DemandAndCapacityNotificationDto updateNotification(@RequestBody DemandAndCapacityNotificationDto dto) {
OwnDemandAndCapacityNotification updatedNotification = ownNotificationService.update(convertToEntity(dto));
if (updatedNotification == null) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Notification does not exist.");
}
return convertToDto(updatedNotification);
}

@DeleteMapping("{id}")
@Operation(summary = "Deletes a notification by its UUID")
@ApiResponses(value = {
@ApiResponse(responseCode = "204", description = "Notification was deleted."),
@ApiResponse(responseCode = "400", description = "Malformed or invalid request body."),
@ApiResponse(responseCode = "404", description = "Notification does not exist."),
@ApiResponse(responseCode = "500", description = "Internal Server Error.")
})
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteNotification(@PathVariable UUID id) {
OwnDemandAndCapacityNotification demand = ownNotificationService.findById(id);
if (demand == null) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Notification does not exist.");
}
ownNotificationService.delete(id);
}

@GetMapping("reported")
@ResponseBody
@Operation(summary = "Get all reported notifications", description = "Get all reported notifications. Optionally the partner can be filtered by its bpnl.")
public List<DemandAndCapacityNotificationDto> getAllReportedNotifications(Optional<@Pattern(regexp = PatternStore.BPNL_STRING) String> partnerBpnl) {
if (partnerBpnl.isEmpty()) {
return reportedNotificationService.findAll().stream().map(this::convertToDto).collect(Collectors.toList());
} else {
return reportedNotificationService.findAllByPartnerBpnl(partnerBpnl.get()).stream().map(this::convertToDto).collect(Collectors.toList());
}
}

private DemandAndCapacityNotificationDto convertToDto(OwnDemandAndCapacityNotification entity) {
DemandAndCapacityNotificationDto dto = modelMapper.map(entity, DemandAndCapacityNotificationDto.class);
if (entity.getMaterials() != null) {
dto.setAffectedMaterialNumbers(entity.getMaterials().stream().map(Material::getOwnMaterialNumber).toList());
}
if (entity.getAffectedSitesRecipient() != null) {
dto.setAffectedSitesBpnsRecipient(entity.getAffectedSitesRecipient().stream().map(Site::getBpns).toList());
}
if (entity.getAffectedSitesSender() != null) {
dto.setAffectedSitesBpnsSender(entity.getAffectedSitesSender().stream().map(Site::getBpns).toList());
}
dto.setPartnerBpnl(entity.getPartner().getBpnl());
dto.setPartnerBpnl(entity.getPartner().getBpnl());
dto.setReported(false);
return dto;
}

private DemandAndCapacityNotificationDto convertToDto(ReportedDemandAndCapacityNotification entity) {
DemandAndCapacityNotificationDto dto = modelMapper.map(entity, DemandAndCapacityNotificationDto.class);
if (entity.getMaterials() != null) {
dto.setAffectedMaterialNumbers(entity.getMaterials().stream().map(Material::getOwnMaterialNumber).toList());
}
if (entity.getAffectedSitesRecipient() != null) {
dto.setAffectedSitesBpnsRecipient(entity.getAffectedSitesRecipient().stream().map(Site::getBpns).toList());
}
if (entity.getAffectedSitesSender() != null) {
dto.setAffectedSitesBpnsSender(entity.getAffectedSitesSender().stream().map(Site::getBpns).toList());
}
dto.setPartnerBpnl(entity.getPartner().getBpnl());
return dto;
}

private OwnDemandAndCapacityNotification convertToEntity(DemandAndCapacityNotificationDto dto) {
OwnDemandAndCapacityNotification entity = modelMapper.map(dto, OwnDemandAndCapacityNotification.class);

Partner existingPartner = partnerService.findByBpnl(dto.getPartnerBpnl());
if (existingPartner == null) {
throw new IllegalStateException(String.format(
"Partner for bpnl %s could not be found",
dto.getPartnerBpnl()));
}
entity.setPartner(existingPartner);

List<Material> materials = new ArrayList<>();
for (String ownMaterialNumber : dto.getAffectedMaterialNumbers()) {
Material material = materialService.findByOwnMaterialNumber(ownMaterialNumber);
if (material == null) {
throw new IllegalStateException(String.format(
"Material for ownMaterialNumber %s could not be found",
ownMaterialNumber));
}
materials.add(material);
}
entity.setMaterials(materials);

List<Site> affectedSitesRecipient = new ArrayList<>();
for (String bpns : dto.getAffectedSitesBpnsRecipient()) {
Site site = existingPartner.getSites().stream().filter(p -> p.getBpns().equals(bpns)).findFirst()
.orElse(null);
if (site == null) {
throw new IllegalStateException(String.format(
"Site for bpns %s could not be found",
bpns));
}
affectedSitesRecipient.add(site);
}
entity.setAffectedSitesRecipient(affectedSitesRecipient);

Partner ownPartner = partnerService.getOwnPartnerEntity();
List<Site> affectedSitesSender = new ArrayList<>();
for (String bpns : dto.getAffectedSitesBpnsSender()) {
Site site = ownPartner.getSites().stream().filter(p -> p.getBpns().equals(bpns)).findFirst().orElse(null);
if (site == null) {
throw new IllegalStateException(String.format(
"Site for bpns %s could not be found",
bpns));
}
affectedSitesSender.add(site);
}
entity.setAffectedSitesSender(affectedSitesSender);

if (dto.getRelatedNotificationId() != null) {
OwnDemandAndCapacityNotification relatedNotification = ownNotificationService.findById(dto.getRelatedNotificationId());
if (relatedNotification == null) {
throw new IllegalStateException(String.format(
"Related notification for UUID %s could not be found",
dto.getRelatedNotificationId()));
}
}

return entity;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
Copyright (c) 2024 Volkswagen AG
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.demandandcapacitynotification.domain.model;

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

import org.eclipse.tractusx.puris.backend.common.util.PatternStore;
import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Material;
import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Partner;
import org.eclipse.tractusx.puris.backend.masterdata.domain.model.Site;

import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.SuperBuilder;

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@SuperBuilder
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@Entity
@ToString
public abstract class DemandAndCapacityNotification {
@Id
@GeneratedValue
protected UUID uuid;

protected UUID relatedNotificationId;

protected UUID sourceNotificationId;

@ManyToOne()
@JoinColumn(name = "partner_uuid")
@ToString.Exclude
@NotNull
protected Partner partner;

@ManyToMany()
@JoinTable(
name = "notification_material",
joinColumns = @JoinColumn(name = "notification_uuid"),
inverseJoinColumns = @JoinColumn(name = "material_ownMaterialNumber"))
@ToString.Exclude
protected List<Material> materials;

@Pattern(regexp = PatternStore.NON_EMPTY_NON_VERTICAL_WHITESPACE_STRING)
protected String text;

@NotNull
protected LeadingRootCauseEnumeration leadingRootCause;

@NotNull
protected EffectEnumeration effect;

@NotNull
protected StatusEnumeration status;

@NotNull
protected Date startDateOfEffect;
protected Date expectedEndDateOfEffect;

@ManyToMany()
@JoinTable(
name = "notification_affected_sites_sender",
joinColumns = @JoinColumn(name = "notification_uuid"),
inverseJoinColumns = @JoinColumn(name = "site_bpns"))
protected List<Site> affectedSitesSender;

@ManyToMany()
@JoinTable(
name = "notification_affected_sites_recipient",
joinColumns = @JoinColumn(name = "notification_uuid"),
inverseJoinColumns = @JoinColumn(name = "site_bpns"))
protected List<Site> affectedSitesRecipient;

@NotNull
protected Date contentChangedAt;
}
Loading

0 comments on commit 9595354

Please sign in to comment.