From 4f9bb6cbca46f051dd33a0bd229a87463a7f9d49 Mon Sep 17 00:00:00 2001 From: Julien Herr Date: Tue, 19 Aug 2025 14:17:54 +0200 Subject: [PATCH 1/4] feat: add api for chargebox/reservation/user --- .../config/ApiAuthenticationManager.java | 6 +- .../steve/ocpp/task/SendLocalListTask.java | 12 +- .../repository/ChargePointRepository.java | 2 +- .../repository/ReservationRepository.java | 2 + .../idsg/steve/repository/UserRepository.java | 5 +- .../impl/ChargePointRepositoryImpl.java | 6 +- .../impl/ReservationRepositoryImpl.java | 30 +++- .../repository/impl/UserRepositoryImpl.java | 39 ++-- .../CentralSystemService16_Service.java | 8 +- .../service/ChargePointServiceClient.java | 7 +- .../steve/service/ChargePointsService.java | 70 ++++++++ ...ppTagService.java => OcppTagsService.java} | 2 +- .../steve/service/ReservationsService.java | 64 +++++++ .../rwth/idsg/steve/service/UsersService.java | 73 ++++++++ ...bUserService.java => WebUsersService.java} | 2 +- .../web/api/ChargePointsRestController.java | 108 ++++++++++++ ...ler.java => ConnectorsRestController.java} | 10 +- .../steve/web/api/OcppTagsRestController.java | 44 ++--- ...java => RemoteCommandsRestController.java} | 10 +- .../web/api/ReservationsRestController.java | 71 ++++++++ ...ntroller.java => TasksRestController.java} | 6 +- .../web/api/TransactionsRestController.java | 10 +- .../steve/web/api/UsersRestController.java | 74 ++++++++ .../steve/web/api/dto/ApiChargePointList.java | 2 +- .../controller/AboutSettingsController.java | 27 ++- .../web/controller/AjaxCallController.java | 34 ++-- .../controller/ChargePointsController.java | 85 +++++---- .../ChargingProfilesController.java | 45 +++-- .../steve/web/controller/HomeController.java | 23 ++- .../steve/web/controller/LogController.java | 5 +- .../web/controller/NoAccessController.java | 1 - .../web/controller/Ocpp12Controller.java | 55 +++--- .../web/controller/Ocpp15Controller.java | 38 ++-- .../web/controller/Ocpp16Controller.java | 41 +++-- .../web/controller/OcppTagsController.java | 32 ++-- ...oller.java => ReservationsController.java} | 62 +++---- .../web/controller/SignOutController.java | 4 +- ...skController.java => TasksController.java} | 29 ++- .../steve/web/controller/UsersController.java | 25 ++- .../web/controller/WebUsersController.java | 30 ++-- .../idsg/steve/web/dto/ReservationForm.java | 49 ++++++ .../steve/utils/__DatabasePreparer__.java | 2 +- .../steve/web/api/AbstractControllerTest.java | 24 ++- .../api/ChargePointsRestControllerTest.java | 166 ++++++++++++++++++ .../web/api/OcppTagsRestControllerTest.java | 105 ++++++----- .../api/ReservationRestControllerTest.java | 51 ++++++ .../api/TransactionRestControllerTest.java | 16 +- .../web/api/UsersRestControllerTest.java | 51 ++++++ 48 files changed, 1219 insertions(+), 444 deletions(-) create mode 100644 src/main/java/de/rwth/idsg/steve/service/ChargePointsService.java rename src/main/java/de/rwth/idsg/steve/service/{OcppTagService.java => OcppTagsService.java} (99%) create mode 100644 src/main/java/de/rwth/idsg/steve/service/ReservationsService.java create mode 100644 src/main/java/de/rwth/idsg/steve/service/UsersService.java rename src/main/java/de/rwth/idsg/steve/service/{WebUserService.java => WebUsersService.java} (99%) create mode 100644 src/main/java/de/rwth/idsg/steve/web/api/ChargePointsRestController.java rename src/main/java/de/rwth/idsg/steve/web/api/{ConnectorRestController.java => ConnectorsRestController.java} (91%) rename src/main/java/de/rwth/idsg/steve/web/api/{RemoteStartStopRestController.java => RemoteCommandsRestController.java} (96%) create mode 100644 src/main/java/de/rwth/idsg/steve/web/api/ReservationsRestController.java rename src/main/java/de/rwth/idsg/steve/web/api/{TaskRestController.java => TasksRestController.java} (96%) create mode 100644 src/main/java/de/rwth/idsg/steve/web/api/UsersRestController.java rename src/main/java/de/rwth/idsg/steve/web/controller/{TransactionsReservationsController.java => ReservationsController.java} (74%) rename src/main/java/de/rwth/idsg/steve/web/controller/{TaskController.java => TasksController.java} (80%) create mode 100644 src/main/java/de/rwth/idsg/steve/web/dto/ReservationForm.java create mode 100644 src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java create mode 100644 src/test/java/de/rwth/idsg/steve/web/api/ReservationRestControllerTest.java create mode 100644 src/test/java/de/rwth/idsg/steve/web/api/UsersRestControllerTest.java diff --git a/src/main/java/de/rwth/idsg/steve/config/ApiAuthenticationManager.java b/src/main/java/de/rwth/idsg/steve/config/ApiAuthenticationManager.java index 09ae34285..b87f8a2a2 100644 --- a/src/main/java/de/rwth/idsg/steve/config/ApiAuthenticationManager.java +++ b/src/main/java/de/rwth/idsg/steve/config/ApiAuthenticationManager.java @@ -20,7 +20,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Strings; -import de.rwth.idsg.steve.service.WebUserService; +import de.rwth.idsg.steve.service.WebUsersService; import de.rwth.idsg.steve.web.api.ApiControllerAdvice; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -52,7 +52,7 @@ @RequiredArgsConstructor public class ApiAuthenticationManager implements AuthenticationManager, AuthenticationEntryPoint { - private final WebUserService webUserService; + private final WebUsersService webUsersService; private final PasswordEncoder passwordEncoder; private final ObjectMapper jacksonObjectMapper; @@ -65,7 +65,7 @@ public Authentication authenticate(Authentication authentication) throws Authent throw new BadCredentialsException("Required parameters missing"); } - UserDetails userDetails = webUserService.loadUserByUsernameForApi(username); + UserDetails userDetails = webUsersService.loadUserByUsernameForApi(username); if (!areValuesSet(userDetails)) { throw new DisabledException("The user does not exist, exists but is disabled or has API access disabled."); } diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/task/SendLocalListTask.java b/src/main/java/de/rwth/idsg/steve/ocpp/task/SendLocalListTask.java index bbc0bc66e..0b87cc371 100644 --- a/src/main/java/de/rwth/idsg/steve/ocpp/task/SendLocalListTask.java +++ b/src/main/java/de/rwth/idsg/steve/ocpp/task/SendLocalListTask.java @@ -20,7 +20,7 @@ import de.rwth.idsg.steve.ocpp.Ocpp15AndAboveTask; import de.rwth.idsg.steve.ocpp.OcppCallback; -import de.rwth.idsg.steve.service.OcppTagService; +import de.rwth.idsg.steve.service.OcppTagsService; import de.rwth.idsg.steve.web.dto.ocpp.SendLocalListParams; import de.rwth.idsg.steve.web.dto.ocpp.SendLocalListUpdateType; import ocpp.cp._2015._10.AuthorizationData; @@ -40,9 +40,9 @@ public class SendLocalListTask extends Ocpp15AndAboveTask getOcpp16Handler(St // Helpers // ------------------------------------------------------------------------- - private ocpp.cp._2015._10.SendLocalListRequest createOcpp16Request(OcppTagService ocppTagService) { + private ocpp.cp._2015._10.SendLocalListRequest createOcpp16Request(OcppTagsService ocppTagsService) { // DIFFERENTIAL update if (params.getUpdateType() == SendLocalListUpdateType.DIFFERENTIAL) { List auths = new ArrayList<>(); @@ -102,7 +102,7 @@ private ocpp.cp._2015._10.SendLocalListRequest createOcpp16Request(OcppTagServic } // Step 2: For the idTags to be added or updated, insert them with their IdTagInfos - auths.addAll(ocppTagService.getAuthData(params.getAddUpdateList())); + auths.addAll(ocppTagsService.getAuthData(params.getAddUpdateList())); return new ocpp.cp._2015._10.SendLocalListRequest() .withListVersion(params.getListVersion()) @@ -114,7 +114,7 @@ private ocpp.cp._2015._10.SendLocalListRequest createOcpp16Request(OcppTagServic List values = Collections.emptyList(); if (Boolean.FALSE.equals(params.getSendEmptyListWhenFull())) { - values = ocppTagService.getAuthDataOfAllTags(); + values = ocppTagsService.getAuthDataOfAllTags(); } return new ocpp.cp._2015._10.SendLocalListRequest() diff --git a/src/main/java/de/rwth/idsg/steve/repository/ChargePointRepository.java b/src/main/java/de/rwth/idsg/steve/repository/ChargePointRepository.java index fbd29d6e8..78248f714 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/ChargePointRepository.java +++ b/src/main/java/de/rwth/idsg/steve/repository/ChargePointRepository.java @@ -51,7 +51,7 @@ default List getChargePointSelect(OcppProtocol protocol, List Map getChargeBoxIdPkPair(List chargeBoxIdList); List getOverview(ChargePointQueryForm form); - ChargePoint.Details getDetails(int chargeBoxPk); + Optional getDetails(int chargeBoxPk); default List getChargePointConnectorStatus() { return getChargePointConnectorStatus(null); diff --git a/src/main/java/de/rwth/idsg/steve/repository/ReservationRepository.java b/src/main/java/de/rwth/idsg/steve/repository/ReservationRepository.java index 80edaf421..f52c43360 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/ReservationRepository.java +++ b/src/main/java/de/rwth/idsg/steve/repository/ReservationRepository.java @@ -25,12 +25,14 @@ import org.jooq.Select; import java.util.List; +import java.util.Optional; /** * @author Sevket Goekay * @since 19.08.2014 */ public interface ReservationRepository { + Optional getReservation(int id); List getReservations(ReservationQueryForm form); List getActiveReservationIds(String chargeBoxId); diff --git a/src/main/java/de/rwth/idsg/steve/repository/UserRepository.java b/src/main/java/de/rwth/idsg/steve/repository/UserRepository.java index 4b06f4d61..f18554c97 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/UserRepository.java +++ b/src/main/java/de/rwth/idsg/steve/repository/UserRepository.java @@ -23,6 +23,7 @@ import de.rwth.idsg.steve.web.dto.UserQueryForm; import java.util.List; +import java.util.Optional; /** * @author Sevket Goekay @@ -30,9 +31,9 @@ */ public interface UserRepository { List getOverview(UserQueryForm form); - User.Details getDetails(int userPk); + Optional getDetails(int userPk); - void add(UserForm form); + Integer add(UserForm form); void update(UserForm form); void delete(int userPk); } diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/ChargePointRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/ChargePointRepositoryImpl.java index 657d02861..8c323576b 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/ChargePointRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/ChargePointRepositoryImpl.java @@ -212,18 +212,18 @@ private Result> getOverviewIn } @Override - public ChargePoint.Details getDetails(int chargeBoxPk) { + public Optional getDetails(int chargeBoxPk) { ChargeBoxRecord cbr = ctx.selectFrom(CHARGE_BOX) .where(CHARGE_BOX.CHARGE_BOX_PK.equal(chargeBoxPk)) .fetchOne(); if (cbr == null) { - throw new SteveException("Charge point not found"); + return Optional.empty(); } AddressRecord ar = addressRepository.get(ctx, cbr.getAddressPk()); - return new ChargePoint.Details(cbr, ar); + return Optional.of(new ChargePoint.Details(cbr, ar)); } @Override diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/ReservationRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/ReservationRepositoryImpl.java index 64e4126ec..7b784d884 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/ReservationRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/ReservationRepositoryImpl.java @@ -25,6 +25,7 @@ import de.rwth.idsg.steve.repository.dto.Reservation; import de.rwth.idsg.steve.utils.DateTimeUtils; import de.rwth.idsg.steve.web.dto.ReservationQueryForm; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.joda.time.DateTime; import org.jooq.DSLContext; @@ -36,10 +37,10 @@ import org.jooq.SelectQuery; import org.jooq.exception.DataAccessException; import org.jooq.impl.DSL; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import java.util.List; +import java.util.Optional; import static jooq.steve.db.tables.ChargeBox.CHARGE_BOX; import static jooq.steve.db.tables.Connector.CONNECTOR; @@ -52,13 +53,34 @@ */ @Slf4j @Repository +@RequiredArgsConstructor public class ReservationRepositoryImpl implements ReservationRepository { private final DSLContext ctx; - @Autowired - public ReservationRepositoryImpl(DSLContext ctx) { - this.ctx = ctx; + @Override + public Optional getReservation(int id) { + SelectQuery selectQuery = ctx.selectQuery(); + selectQuery.addFrom(RESERVATION); + selectQuery.addJoin(OCPP_TAG, OCPP_TAG.ID_TAG.eq(RESERVATION.ID_TAG)); + selectQuery.addJoin(CONNECTOR, CONNECTOR.CONNECTOR_PK.eq(RESERVATION.CONNECTOR_PK)); + selectQuery.addJoin(CHARGE_BOX, CONNECTOR.CHARGE_BOX_ID.eq(CHARGE_BOX.CHARGE_BOX_ID)); + + selectQuery.addSelect( + RESERVATION.RESERVATION_PK, + RESERVATION.TRANSACTION_PK, + OCPP_TAG.OCPP_TAG_PK, + CHARGE_BOX.CHARGE_BOX_PK, + OCPP_TAG.ID_TAG, + CHARGE_BOX.CHARGE_BOX_ID, + RESERVATION.START_DATETIME, + RESERVATION.EXPIRY_DATETIME, + RESERVATION.STATUS, + CONNECTOR.CONNECTOR_ID + ); + selectQuery.addConditions(RESERVATION.RESERVATION_PK.eq(id)); + Reservation result = (Reservation) selectQuery.fetchOne(new ReservationMapper()); + return Optional.ofNullable(result); } @Override diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/UserRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/UserRepositoryImpl.java index e3b118efc..a332cdb2b 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/UserRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/UserRepositoryImpl.java @@ -37,14 +37,10 @@ import org.jooq.SelectConditionStep; import org.jooq.exception.DataAccessException; import org.jooq.impl.DSL; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import org.springframework.util.CollectionUtils; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import static de.rwth.idsg.steve.utils.CustomDSL.includes; import static jooq.steve.db.Tables.USER_OCPP_TAG; @@ -79,31 +75,31 @@ public List getOverview(UserQueryForm form) { } @Override - public User.Details getDetails(int userPk) { - UserRecord ur = ctx.selectFrom(USER) + public Optional getDetails(int userPk) { + var ur = ctx.selectFrom(USER) .where(USER.USER_PK.equal(userPk)) .fetchOne(); if (ur == null) { - throw new SteveException("There is no user with id '%s'", userPk); + return Optional.empty(); } - return User.Details.builder() + return Optional.of(User.Details.builder() .userRecord(ur) .address(addressRepository.get(ctx, ur.getAddressPk())) .ocppTagEntries(getOcppTagsInternal(userPk, null).getOrDefault(userPk, List.of())) - .build(); + .build()); } @Override - public void add(UserForm form) { - ctx.transaction(configuration -> { - DSLContext ctx = DSL.using(configuration); + public Integer add(UserForm form) { + return ctx.transactionResult(configuration -> { + var ctx = DSL.using(configuration); try { - Integer addressId = addressRepository.updateOrInsert(ctx, form.getAddress()); - Integer userPk = addInternal(ctx, form, addressId); + var addressId = addressRepository.updateOrInsert(ctx, form.getAddress()); + var userPk = addInternal(ctx, form, addressId); refreshOcppTagsInternal(ctx, form, userPk); - + return userPk; } catch (DataAccessException e) { throw new SteveException("Failed to add the user", e); } @@ -113,9 +109,9 @@ public void add(UserForm form) { @Override public void update(UserForm form) { ctx.transaction(configuration -> { - DSLContext ctx = DSL.using(configuration); + var ctx = DSL.using(configuration); try { - Integer addressId = addressRepository.updateOrInsert(ctx, form.getAddress()); + var addressId = addressRepository.updateOrInsert(ctx, form.getAddress()); updateInternal(ctx, form, addressId); refreshOcppTagsInternal(ctx, form, form.getUserPk()); @@ -128,11 +124,10 @@ public void update(UserForm form) { @Override public void delete(int userPk) { ctx.transaction(configuration -> { - DSLContext ctx = DSL.using(configuration); + var ctx = DSL.using(configuration); try { addressRepository.delete(ctx, selectAddressId(userPk)); deleteInternal(ctx, userPk); - } catch (DataAccessException e) { throw new SteveException("Failed to delete the user", e); } @@ -186,7 +181,7 @@ private Result> getOverviewInte } private Map> getOcppTagsInternal(Integer userPk, String ocppIdTag) { - List conditions = new ArrayList<>(); + var conditions = new ArrayList(); if (userPk != null) { conditions.add(USER_OCPP_TAG.USER_PK.eq(userPk)); @@ -205,7 +200,7 @@ private Map> getOcppTagsInternal(Integer userPk .where(conditions) .fetch(); - Map> map = new HashMap<>(); + var map = new HashMap>(); for (var entry : results) { map.computeIfAbsent(entry.value1(), k -> new ArrayList<>()) .add(new User.OcppTagEntry(entry.value2(), entry.value3())); diff --git a/src/main/java/de/rwth/idsg/steve/service/CentralSystemService16_Service.java b/src/main/java/de/rwth/idsg/steve/service/CentralSystemService16_Service.java index 45df16c99..565d6cb1b 100644 --- a/src/main/java/de/rwth/idsg/steve/service/CentralSystemService16_Service.java +++ b/src/main/java/de/rwth/idsg/steve/service/CentralSystemService16_Service.java @@ -74,7 +74,7 @@ public class CentralSystemService16_Service { @Autowired private OcppServerRepository ocppServerRepository; @Autowired private SettingsRepository settingsRepository; - @Autowired private OcppTagService ocppTagService; + @Autowired private OcppTagsService ocppTagsService; @Autowired private ApplicationEventPublisher applicationEventPublisher; @Autowired private ChargePointHelperService chargePointHelperService; @@ -172,7 +172,7 @@ public DiagnosticsStatusNotificationResponse diagnosticsStatusNotification( public StartTransactionResponse startTransaction(StartTransactionRequest parameters, String chargeBoxIdentity) { // Get the authorization info of the user, before making tx changes (will affectAuthorizationStatus) - IdTagInfo info = ocppTagService.getIdTagInfo( + IdTagInfo info = ocppTagsService.getIdTagInfo( parameters.getIdTag(), true, chargeBoxIdentity, @@ -205,7 +205,7 @@ public StopTransactionResponse stopTransaction(StopTransactionRequest parameters String stopReason = parameters.isSetReason() ? parameters.getReason().value() : null; // Get the authorization info of the user, before making tx changes (will affectAuthorizationStatus) - IdTagInfo idTagInfo = ocppTagService.getIdTagInfo( + IdTagInfo idTagInfo = ocppTagsService.getIdTagInfo( parameters.getIdTag(), false, chargeBoxIdentity, @@ -242,7 +242,7 @@ public HeartbeatResponse heartbeat(HeartbeatRequest parameters, String chargeBox public AuthorizeResponse authorize(AuthorizeRequest parameters, String chargeBoxIdentity) { // Get the authorization info of the user - IdTagInfo idTagInfo = ocppTagService.getIdTagInfo( + IdTagInfo idTagInfo = ocppTagsService.getIdTagInfo( parameters.getIdTag(), false, chargeBoxIdentity, diff --git a/src/main/java/de/rwth/idsg/steve/service/ChargePointServiceClient.java b/src/main/java/de/rwth/idsg/steve/service/ChargePointServiceClient.java index 958e74d73..fccb1782f 100644 --- a/src/main/java/de/rwth/idsg/steve/service/ChargePointServiceClient.java +++ b/src/main/java/de/rwth/idsg/steve/service/ChargePointServiceClient.java @@ -68,7 +68,6 @@ import de.rwth.idsg.steve.web.dto.ocpp.UpdateFirmwareParams; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import ocpp.cp._2015._10.ChargingProfilePurposeType; import ocpp.cp._2015._10.GetCompositeScheduleResponse; import org.joda.time.DateTime; import org.springframework.stereotype.Service; @@ -86,7 +85,7 @@ public class ChargePointServiceClient { private final ChargingProfileRepository chargingProfileRepository; private final ReservationRepository reservationRepository; - private final OcppTagService ocppTagService; + private final OcppTagsService ocppTagsService; private final DelegatingTaskExecutor asyncTaskExecutor; private final TaskStore taskStore; @@ -333,7 +332,7 @@ public final int getLocalListVersion(MultipleChargePointSelect params, @SafeVarargs public final int sendLocalList(SendLocalListParams params, OcppCallback... callbacks) { - SendLocalListTask task = new SendLocalListTask(params, ocppTagService); + SendLocalListTask task = new SendLocalListTask(params, ocppTagsService); for (var callback : callbacks) { task.addCallback(callback); @@ -364,7 +363,7 @@ public final int reserveNow(ReserveNowParams params, .build(); int reservationId = reservationRepository.insert(res); - String parentIdTag = ocppTagService.getParentIdtag(params.getIdTag()); + String parentIdTag = ocppTagsService.getParentIdtag(params.getIdTag()); EnhancedReserveNowParams enhancedParams = new EnhancedReserveNowParams(params, reservationId, parentIdTag); ReserveNowTask task = new ReserveNowTask(enhancedParams, reservationRepository); diff --git a/src/main/java/de/rwth/idsg/steve/service/ChargePointsService.java b/src/main/java/de/rwth/idsg/steve/service/ChargePointsService.java new file mode 100644 index 000000000..d52d22c6c --- /dev/null +++ b/src/main/java/de/rwth/idsg/steve/service/ChargePointsService.java @@ -0,0 +1,70 @@ +/* + * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve + * Copyright (C) 2013-2025 SteVe Community Team + * All Rights Reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.rwth.idsg.steve.service; + +import de.rwth.idsg.steve.repository.ChargePointRepository; +import de.rwth.idsg.steve.repository.dto.ChargePoint; +import de.rwth.idsg.steve.web.api.exception.NotFoundException; +import de.rwth.idsg.steve.web.dto.ChargePointForm; +import de.rwth.idsg.steve.web.dto.ChargePointQueryForm; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class ChargePointsService { + + private final ChargePointRepository chargePointRepository; + + public List getOverview(ChargePointQueryForm form) { + return chargePointRepository.getOverview(form); + } + + public ChargePoint.Details getDetails(int chargeBoxPk) { + return chargePointRepository.getDetails(chargeBoxPk).orElseThrow( + () -> new NotFoundException("Charge Point with ID " + chargeBoxPk + " not found") + ); + } + + public int addChargePoint(ChargePointForm form) { + return chargePointRepository.addChargePoint(form); + } + + public void updateChargePoint(ChargePointForm form) { + chargePointRepository.updateChargePoint(form); + } + + public void deleteChargePoint(int chargeBoxPk) { + chargePointRepository.deleteChargePoint(chargeBoxPk); + } + + public List getChargeBoxIds() { + return chargePointRepository.getChargeBoxIds(); + } + + public void addChargePointList(List chargeBoxIdList) { + chargePointRepository.addChargePointList(chargeBoxIdList); + } + + public List getNonZeroConnectorIds(String chargeBoxId) { + return chargePointRepository.getNonZeroConnectorIds(chargeBoxId); + } +} diff --git a/src/main/java/de/rwth/idsg/steve/service/OcppTagService.java b/src/main/java/de/rwth/idsg/steve/service/OcppTagsService.java similarity index 99% rename from src/main/java/de/rwth/idsg/steve/service/OcppTagService.java rename to src/main/java/de/rwth/idsg/steve/service/OcppTagsService.java index 61e32a513..980ce7218 100644 --- a/src/main/java/de/rwth/idsg/steve/service/OcppTagService.java +++ b/src/main/java/de/rwth/idsg/steve/service/OcppTagsService.java @@ -48,7 +48,7 @@ @Slf4j @Service @RequiredArgsConstructor -public class OcppTagService { +public class OcppTagsService { private final UnidentifiedIncomingObjectService invalidOcppTagService = new UnidentifiedIncomingObjectService(1000); diff --git a/src/main/java/de/rwth/idsg/steve/service/ReservationsService.java b/src/main/java/de/rwth/idsg/steve/service/ReservationsService.java new file mode 100644 index 000000000..aa39a7085 --- /dev/null +++ b/src/main/java/de/rwth/idsg/steve/service/ReservationsService.java @@ -0,0 +1,64 @@ +/* + * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve + * Copyright (C) 2013-2025 SteVe Community Team + * All Rights Reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.rwth.idsg.steve.service; + +import de.rwth.idsg.steve.repository.ReservationRepository; +import de.rwth.idsg.steve.repository.dto.InsertReservationParams; +import de.rwth.idsg.steve.repository.dto.Reservation; +import de.rwth.idsg.steve.web.dto.ReservationForm; +import de.rwth.idsg.steve.web.dto.ReservationQueryForm; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; + +@Service +@RequiredArgsConstructor +public class ReservationsService { + + private final ReservationRepository reservationRepository; + + public List getReservations(ReservationQueryForm form) { + return reservationRepository.getReservations(form); + } + + public int addReservation(ReservationForm form) { + var params = InsertReservationParams.builder() + .idTag(form.getIdTag()) + .chargeBoxId(form.getChargeBoxId()) + .connectorId(form.getConnectorId()) + .startTimestamp(form.getStartTimestamp()) + .expiryTimestamp(form.getExpiryTimestamp()) + .build(); + return reservationRepository.insert(params); + } + + public void deleteReservation(int reservationId) { + reservationRepository.delete(reservationId); + } + + public Optional getReservation(int id) { + return reservationRepository.getReservation(id); + } + + public List getActiveReservationIds(String chargeBoxId) { + return reservationRepository.getActiveReservationIds(chargeBoxId); + } +} diff --git a/src/main/java/de/rwth/idsg/steve/service/UsersService.java b/src/main/java/de/rwth/idsg/steve/service/UsersService.java new file mode 100644 index 000000000..2fafa84eb --- /dev/null +++ b/src/main/java/de/rwth/idsg/steve/service/UsersService.java @@ -0,0 +1,73 @@ +/* + * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve + * Copyright (C) 2013-2025 SteVe Community Team + * All Rights Reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.rwth.idsg.steve.service; + +import de.rwth.idsg.steve.SteveException; +import de.rwth.idsg.steve.repository.UserRepository; +import de.rwth.idsg.steve.repository.dto.User; +import de.rwth.idsg.steve.web.api.exception.NotFoundException; +import de.rwth.idsg.steve.web.dto.UserForm; +import de.rwth.idsg.steve.web.dto.UserQueryForm; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class UsersService { + + private final UserRepository userRepository; + + public List getUsers(UserQueryForm form) { + return userRepository.getOverview(form); + } + + public User.Details getDetails(int userPk) { + return userRepository.getDetails(userPk).orElseThrow( + () -> new NotFoundException(String.format("User with id %d not found", userPk)) + ); + } + + public User.Details add(UserForm form) { + var id = userRepository.add(form); + return userRepository.getDetails(id).orElseThrow( + () -> new SteveException("User not found after creation, this should never happen") + ); + } + + public User.Details update(UserForm form) { + userRepository.update(form); + return userRepository.getDetails(form.getUserPk()).orElseThrow( + () -> new SteveException("User not found after update, this should never happen") + ); + } + + public User.Details delete(int userPk) { + var user = userRepository.getDetails(userPk).orElseThrow( + () -> new NotFoundException(String.format("User with id %d not found", userPk)) + ); + userRepository.delete(userPk); + return user; + } + + public List getOverview(UserQueryForm params) { + return userRepository.getOverview(params); + } +} diff --git a/src/main/java/de/rwth/idsg/steve/service/WebUserService.java b/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java similarity index 99% rename from src/main/java/de/rwth/idsg/steve/service/WebUserService.java rename to src/main/java/de/rwth/idsg/steve/service/WebUsersService.java index 55474560f..43cfd0057 100644 --- a/src/main/java/de/rwth/idsg/steve/service/WebUserService.java +++ b/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java @@ -70,7 +70,7 @@ */ @Service @RequiredArgsConstructor -public class WebUserService implements UserDetailsManager { +public class WebUsersService implements UserDetailsManager { // Because Guava's cache does not accept a null value private static final UserDetails DUMMY_USER = new User("#", "#", Collections.emptyList()); diff --git a/src/main/java/de/rwth/idsg/steve/web/api/ChargePointsRestController.java b/src/main/java/de/rwth/idsg/steve/web/api/ChargePointsRestController.java new file mode 100644 index 000000000..ec245db00 --- /dev/null +++ b/src/main/java/de/rwth/idsg/steve/web/api/ChargePointsRestController.java @@ -0,0 +1,108 @@ +/* + * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve + * Copyright (C) 2013-2025 SteVe Community Team + * All Rights Reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.rwth.idsg.steve.web.api; + +import de.rwth.idsg.steve.repository.dto.ChargePoint; +import de.rwth.idsg.steve.service.ChargePointsService; +import de.rwth.idsg.steve.web.dto.ChargePointForm; +import de.rwth.idsg.steve.web.dto.ChargePointQueryForm; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import org.springdoc.core.annotations.ParameterObject; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@Tag(name = "charge-points", description = "Operations related to managing ChargePoints.") +@RestController +@RequestMapping(value = {"/api/v1/chargepoints", "/api/v1/chargeboxes"}, produces = MediaType.APPLICATION_JSON_VALUE) +@RequiredArgsConstructor +public class ChargePointsRestController { + + private final ChargePointsService chargePointsService; + + @Operation(description = "Returns a list of ChargePoints based on the query parameters.") + @StandardApiResponses + @GetMapping + public List get(@ParameterObject ChargePointQueryForm params) { + return chargePointsService.getOverview(params).stream().map(ChargePointsRestController::toDto).toList(); + } + + @Operation(description = "Returns a single ChargePoint based on the ChargePointPk.") + @StandardApiResponses + @GetMapping("/{chargePointPk}") + public ApiChargePoint getOne(@PathVariable("chargePointPk") Integer chargePointPk) { + return toDto(chargePointsService.getDetails(chargePointPk)); + } + + @Operation(description = "Creates a new ChargePoint with the provided parameters.") + @StandardApiResponses + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public ApiChargePoint create(@RequestBody @Valid ChargePointForm params) { + var chargepointPk = chargePointsService.addChargePoint(params); + return toDto(chargePointsService.getDetails(chargepointPk)); + } + + @Operation(description = "Updates an existing ChargePoint with the provided parameters.") + @StandardApiResponses + @PutMapping("/{chargePointPk}") + public ApiChargePoint update(@PathVariable("chargePointPk") Integer chargePointPk, + @RequestBody @Valid ChargePointForm params) { + params.setChargeBoxPk(chargePointPk); + chargePointsService.updateChargePoint(params); + return toDto(chargePointsService.getDetails(chargePointPk)); + } + + @Operation(description = "Deletes an existing Chargepoint based on the ChargePointPk.") + @StandardApiResponses + @DeleteMapping("/{chargePointPk}") + public ApiChargePoint delete(@PathVariable("chargePointPk") Integer chargePointPk) { + var response = chargePointsService.getDetails(chargePointPk); + chargePointsService.deleteChargePoint(chargePointPk); + return toDto(response); + } + + private static ApiChargePoint toDto(ChargePoint.Overview overview) { + return new ApiChargePoint(overview.getChargeBoxPk(), overview.getChargeBoxId()); + } + + private static ApiChargePoint toDto(ChargePoint.Details details) { + return new ApiChargePoint(details.getChargeBox().getChargeBoxPk(), details.getChargeBox().getChargeBoxId()); + } + + @Data + public static class ApiChargePoint { + private final Integer chargeBoxPk; + private final String chargeBoxId; + } +} diff --git a/src/main/java/de/rwth/idsg/steve/web/api/ConnectorRestController.java b/src/main/java/de/rwth/idsg/steve/web/api/ConnectorsRestController.java similarity index 91% rename from src/main/java/de/rwth/idsg/steve/web/api/ConnectorRestController.java rename to src/main/java/de/rwth/idsg/steve/web/api/ConnectorsRestController.java index c76b0cad5..eacbb4b00 100644 --- a/src/main/java/de/rwth/idsg/steve/web/api/ConnectorRestController.java +++ b/src/main/java/de/rwth/idsg/steve/web/api/ConnectorsRestController.java @@ -18,14 +18,15 @@ */ package de.rwth.idsg.steve.web.api; -import de.rwth.idsg.steve.repository.ChargePointRepository; import de.rwth.idsg.steve.repository.dto.ConnectorStatus; import de.rwth.idsg.steve.service.ChargePointHelperService; +import de.rwth.idsg.steve.service.ChargePointsService; import de.rwth.idsg.steve.utils.ConnectorStatusFilter; import de.rwth.idsg.steve.web.api.dto.ApiConnectorList; import de.rwth.idsg.steve.web.dto.ConnectorStatusForm; import de.rwth.idsg.steve.web.dto.OcppJsonStatus; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.web.bind.annotation.RequestMapping; import java.util.List; @@ -42,13 +43,14 @@ * @author fnkbsi * @since 20.10.2023 */ +@Tag(name = "connectors") @Slf4j @RestController @RequestMapping(value = "/api/v1/connectors", produces = MediaType.APPLICATION_JSON_VALUE) @RequiredArgsConstructor -public class ConnectorRestController { +public class ConnectorsRestController { - private final ChargePointRepository chargePointRepository; + private final ChargePointsService chargePointsService; private final ChargePointHelperService chargePointHelperService; // ------------------------------------------------------------------------- @@ -59,7 +61,7 @@ public class ConnectorRestController { @GetMapping(value = "") public ApiConnectorList getConnectors(@Valid ConnectorStatusForm queryParams) { var conList = new ApiConnectorList(); - conList.setChargeBoxList(chargePointRepository.getChargeBoxIds()); + conList.setChargeBoxList(chargePointsService.getChargeBoxIds()); conList.setFiltered(isFiltered(queryParams)); var latestList = chargePointHelperService.getChargePointConnectorStatus(queryParams); diff --git a/src/main/java/de/rwth/idsg/steve/web/api/OcppTagsRestController.java b/src/main/java/de/rwth/idsg/steve/web/api/OcppTagsRestController.java index 722694611..4e80521f4 100644 --- a/src/main/java/de/rwth/idsg/steve/web/api/OcppTagsRestController.java +++ b/src/main/java/de/rwth/idsg/steve/web/api/OcppTagsRestController.java @@ -18,9 +18,8 @@ */ package de.rwth.idsg.steve.web.api; -import de.rwth.idsg.steve.SteveException; import de.rwth.idsg.steve.repository.dto.OcppTag.OcppTagOverview; -import de.rwth.idsg.steve.service.OcppTagService; +import de.rwth.idsg.steve.service.OcppTagsService; import de.rwth.idsg.steve.web.api.ApiControllerAdvice.ApiErrorResponse; import de.rwth.idsg.steve.web.api.exception.NotFoundException; import de.rwth.idsg.steve.web.dto.OcppTagForm; @@ -32,7 +31,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springdoc.core.annotations.ParameterObject; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -53,7 +51,7 @@ * @author Sevket Goekay * @since 13.09.2022 */ -@Tag(name = "ocpp-tag-controller", +@Tag(name = "tags", description = """ Operations related to managing Ocpp Tags. An Ocpp Tag is the identifier of the actor that interacts with the charge box. @@ -61,13 +59,12 @@ An RFID card is an example of an Ocpp Tag. """ ) -@Slf4j @RestController @RequestMapping(value = "/api/v1/ocppTags", produces = MediaType.APPLICATION_JSON_VALUE) @RequiredArgsConstructor public class OcppTagsRestController { - private final OcppTagService ocppTagService; + private final OcppTagsService ocppTagsService; @Operation(description = """ Returns a list of Ocpp Tags based on the query parameters. @@ -76,11 +73,7 @@ public class OcppTagsRestController { @StandardApiResponses @GetMapping(value = "") public List get(@ParameterObject OcppTagQueryFormForApi params) { - log.debug("Read request for query: {}", params); - - var response = ocppTagService.getOverview(params); - log.debug("Read response for query: {}", response); - return response; + return ocppTagsService.getOverview(params); } @Operation(description = """ @@ -89,11 +82,7 @@ public List get(@ParameterObject OcppTagQueryFormForApi params) @StandardApiResponses @GetMapping("/{ocppTagPk}") public OcppTagOverview getOne(@PathVariable("ocppTagPk") Integer ocppTagPk) { - log.debug("Read request for ocppTagPk: {}", ocppTagPk); - - var response = getOneInternal(ocppTagPk); - log.debug("Read response: {}", response); - return response; + return getOneInternal(ocppTagPk); } @Operation(description = """ @@ -111,13 +100,9 @@ public OcppTagOverview getOne(@PathVariable("ocppTagPk") Integer ocppTagPk) { @PostMapping @ResponseStatus(HttpStatus.CREATED) public OcppTagOverview create(@RequestBody @Valid OcppTagForm params) { - log.debug("Create request: {}", params); - - int ocppTagPk = ocppTagService.addOcppTag(params); + int ocppTagPk = ocppTagsService.addOcppTag(params); - var response = getOneInternal(ocppTagPk); - log.debug("Create response: {}", response); - return response; + return getOneInternal(ocppTagPk); } @Operation(description = """ @@ -127,13 +112,9 @@ public OcppTagOverview create(@RequestBody @Valid OcppTagForm params) { @PutMapping("/{ocppTagPk}") public OcppTagOverview update(@PathVariable("ocppTagPk") Integer ocppTagPk, @RequestBody @Valid OcppTagForm params) { params.setOcppTagPk(ocppTagPk); // the one from incoming params does not matter - log.debug("Update request: {}", params); - - ocppTagService.updateOcppTag(params); + ocppTagsService.updateOcppTag(params); - var response = getOneInternal(ocppTagPk); - log.debug("Update response: {}", response); - return response; + return getOneInternal(ocppTagPk); } @Operation(description = """ @@ -143,12 +124,9 @@ public OcppTagOverview update(@PathVariable("ocppTagPk") Integer ocppTagPk, @Req @StandardApiResponses @DeleteMapping("/{ocppTagPk}") public OcppTagOverview delete(@PathVariable("ocppTagPk") Integer ocppTagPk) { - log.debug("Delete request for ocppTagPk: {}", ocppTagPk); - var response = getOneInternal(ocppTagPk); - ocppTagService.deleteOcppTag(ocppTagPk); + ocppTagsService.deleteOcppTag(ocppTagPk); - log.debug("Delete response: {}", response); return response; } @@ -156,7 +134,7 @@ private OcppTagOverview getOneInternal(int ocppTagPk) { var params = new OcppTagQueryFormForApi(); params.setOcppTagPk(ocppTagPk); - var results = ocppTagService.getOverview(params); + var results = ocppTagsService.getOverview(params); if (results.isEmpty()) { throw new NotFoundException("Could not find this ocppTag"); } diff --git a/src/main/java/de/rwth/idsg/steve/web/api/RemoteStartStopRestController.java b/src/main/java/de/rwth/idsg/steve/web/api/RemoteCommandsRestController.java similarity index 96% rename from src/main/java/de/rwth/idsg/steve/web/api/RemoteStartStopRestController.java rename to src/main/java/de/rwth/idsg/steve/web/api/RemoteCommandsRestController.java index c86a5c956..25ea554fd 100644 --- a/src/main/java/de/rwth/idsg/steve/web/api/RemoteStartStopRestController.java +++ b/src/main/java/de/rwth/idsg/steve/web/api/RemoteCommandsRestController.java @@ -23,6 +23,7 @@ import de.rwth.idsg.steve.repository.TaskStore; import de.rwth.idsg.steve.repository.TransactionRepository; +import de.rwth.idsg.steve.repository.dto.ChargePointSelect; import de.rwth.idsg.steve.service.ChargePointHelperService; import de.rwth.idsg.steve.service.ChargePointServiceClient; @@ -36,6 +37,7 @@ import de.rwth.idsg.steve.web.api.dto.ApiChargePointList; import de.rwth.idsg.steve.web.api.dto.ApiChargePointStart; import de.rwth.idsg.steve.web.api.exception.BadRequestException; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.MediaType; @@ -46,18 +48,19 @@ import jakarta.validation.Valid; +import java.util.ArrayList; import java.util.Collections; /** * @author fnkbsi * @since 18.10.2023 */ - +@Tag(name = "remote-commands") @Slf4j @RestController @RequestMapping(value = "/api/v1/remote", produces = MediaType.APPLICATION_JSON_VALUE) @RequiredArgsConstructor -public class RemoteStartStopRestController { +public class RemoteCommandsRestController { private static final String CALLER = "SteveWebApi"; @@ -72,7 +75,8 @@ public class RemoteStartStopRestController { // ------------------------------------------------------------------------- private ApiChargePointList getChargePoints() { - var chargePoints = chargePointHelperService.getChargePoints(OcppVersion.V_12); + var chargePoints = new ArrayList(); + chargePoints.addAll(chargePointHelperService.getChargePoints(OcppVersion.V_12)); chargePoints.addAll(chargePointHelperService.getChargePoints(OcppVersion.V_15)); chargePoints.addAll(chargePointHelperService.getChargePoints(OcppVersion.V_16)); var lsCp = new ApiChargePointList(); diff --git a/src/main/java/de/rwth/idsg/steve/web/api/ReservationsRestController.java b/src/main/java/de/rwth/idsg/steve/web/api/ReservationsRestController.java new file mode 100644 index 000000000..9e10e70a4 --- /dev/null +++ b/src/main/java/de/rwth/idsg/steve/web/api/ReservationsRestController.java @@ -0,0 +1,71 @@ +/* + * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve + * Copyright (C) 2013-2025 SteVe Community Team + * All Rights Reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.rwth.idsg.steve.web.api; + +import de.rwth.idsg.steve.SteveException; +import de.rwth.idsg.steve.repository.dto.Reservation; +import de.rwth.idsg.steve.service.ReservationsService; +import de.rwth.idsg.steve.web.api.exception.NotFoundException; +import de.rwth.idsg.steve.web.dto.ReservationForm; +import de.rwth.idsg.steve.web.dto.ReservationQueryForm; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; + +import jakarta.validation.Valid; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@Tag(name = "reservations") +@RestController +@RequestMapping(path = "/api/reservations", produces = MediaType.APPLICATION_JSON_VALUE) +@RequiredArgsConstructor +public class ReservationsRestController { + + private final ReservationsService reservationsService; + + @GetMapping + public List getReservations(ReservationQueryForm form) { + return reservationsService.getReservations(form); + } + + @PostMapping + public Reservation addReservation(@Valid @RequestBody ReservationForm form) { + var id = reservationsService.addReservation(form); + return reservationsService.getReservation(id).orElseThrow( + () -> new SteveException("Reservation not found after creation, this should never happen") + ); + } + + @DeleteMapping("/{id}") + public Reservation deleteReservation(@PathVariable int id) { + var reservation = reservationsService.getReservation(id).orElseThrow( + () -> new NotFoundException(String.format("Reservation with id %d not found", id)) + ); + reservationsService.deleteReservation(id); + return reservation; + } +} diff --git a/src/main/java/de/rwth/idsg/steve/web/api/TaskRestController.java b/src/main/java/de/rwth/idsg/steve/web/api/TasksRestController.java similarity index 96% rename from src/main/java/de/rwth/idsg/steve/web/api/TaskRestController.java rename to src/main/java/de/rwth/idsg/steve/web/api/TasksRestController.java index cbc8eb923..87e09a9d7 100644 --- a/src/main/java/de/rwth/idsg/steve/web/api/TaskRestController.java +++ b/src/main/java/de/rwth/idsg/steve/web/api/TasksRestController.java @@ -23,10 +23,10 @@ import de.rwth.idsg.steve.web.api.dto.ApiTaskInfo; import de.rwth.idsg.steve.web.api.dto.ApiTaskList; import de.rwth.idsg.steve.web.api.exception.NotFoundException; +import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.http.MediaType; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; @@ -41,12 +41,12 @@ * @author fnkbsi * @since 18.10.2023 */ -@Slf4j +@Tag(name = "tasks") @Validated @RestController @RequestMapping(value = "/api/v1/tasks", produces = MediaType.APPLICATION_JSON_VALUE) @RequiredArgsConstructor -public class TaskRestController { +public class TasksRestController { private final TaskStore taskStore; diff --git a/src/main/java/de/rwth/idsg/steve/web/api/TransactionsRestController.java b/src/main/java/de/rwth/idsg/steve/web/api/TransactionsRestController.java index dcf801a22..4c4be0576 100644 --- a/src/main/java/de/rwth/idsg/steve/web/api/TransactionsRestController.java +++ b/src/main/java/de/rwth/idsg/steve/web/api/TransactionsRestController.java @@ -25,7 +25,6 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springdoc.core.annotations.ParameterObject; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; @@ -39,13 +38,12 @@ * @author Sevket Goekay * @since 13.09.2022 */ -@Tag(name = "transaction-controller", +@Tag(name = "transactions", description = """ Operations related to querying transactions. A transaction represents a charging session at a charge box (i.e. charging station. The notions 'charge box' and 'charging station' are being used interchangeably). """ ) -@Slf4j @RestController @RequestMapping(value = "/api/v1/transactions", produces = MediaType.APPLICATION_JSON_VALUE) @RequiredArgsConstructor @@ -60,14 +58,10 @@ public class TransactionsRestController { @StandardApiResponses @GetMapping(value = "") public List get(@Valid @ParameterObject TransactionQueryForm.TransactionQueryFormForApi params) { - log.debug("Read request for query: {}", params); - if (params.isReturnCSV()) { throw new BadRequestException("returnCSV=true is not supported for API calls"); } - var response = transactionRepository.getTransactions(params); - log.debug("Read response for query: {}", response); - return response; + return transactionRepository.getTransactions(params); } } diff --git a/src/main/java/de/rwth/idsg/steve/web/api/UsersRestController.java b/src/main/java/de/rwth/idsg/steve/web/api/UsersRestController.java new file mode 100644 index 000000000..a0d216b26 --- /dev/null +++ b/src/main/java/de/rwth/idsg/steve/web/api/UsersRestController.java @@ -0,0 +1,74 @@ +/* + * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve + * Copyright (C) 2013-2025 SteVe Community Team + * All Rights Reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.rwth.idsg.steve.web.api; + +import de.rwth.idsg.steve.repository.dto.User; +import de.rwth.idsg.steve.service.UsersService; +import de.rwth.idsg.steve.web.dto.UserForm; +import de.rwth.idsg.steve.web.dto.UserQueryForm; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; + +import jakarta.validation.Valid; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@Tag(name = "users") +@RestController +@RequestMapping(path = "/api/users", produces = MediaType.APPLICATION_JSON_VALUE) +@RequiredArgsConstructor +public class UsersRestController { + + private final UsersService usersService; + + @GetMapping + public List getUsers(UserQueryForm form) { + return usersService.getUsers(form); + } + + @GetMapping("/{id}") + public User.Details getUser(@PathVariable int id) { + return usersService.getDetails(id); + } + + @PostMapping + public User.Details addUser(@Valid @RequestBody UserForm form) { + return usersService.add(form); + } + + @PutMapping("/{id}") + public User.Details updateUser(@PathVariable int id, @Valid @RequestBody UserForm form) { + form.setUserPk(id); + return usersService.update(form); + } + + @DeleteMapping("/{id}") + public User.Details deleteUser(@PathVariable int id) { + return usersService.delete(id); + } +} diff --git a/src/main/java/de/rwth/idsg/steve/web/api/dto/ApiChargePointList.java b/src/main/java/de/rwth/idsg/steve/web/api/dto/ApiChargePointList.java index 7d0b252ed..b0b275dd0 100644 --- a/src/main/java/de/rwth/idsg/steve/web/api/dto/ApiChargePointList.java +++ b/src/main/java/de/rwth/idsg/steve/web/api/dto/ApiChargePointList.java @@ -41,7 +41,7 @@ public void addCP(String chargeBoxId, List connectorIds) { @Getter @RequiredArgsConstructor - static class ChargePointInfo { + public static class ChargePointInfo { @Schema(description = "Charge Box ID") private final String chargeBoxId; @Schema(description = "List of the charge box connectors") diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java b/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java index c56b10a43..de4cc889b 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java @@ -25,15 +25,13 @@ import de.rwth.idsg.steve.service.ReleaseCheckService; import de.rwth.idsg.steve.web.dto.EndpointInfo; import de.rwth.idsg.steve.web.dto.SettingsForm; +import lombok.RequiredArgsConstructor; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.*; import jakarta.validation.Valid; @@ -46,13 +44,14 @@ */ @Controller @RequestMapping(value = "/manager") +@RequiredArgsConstructor public class AboutSettingsController { - @Autowired private GenericRepository genericRepository; - @Autowired private LogController logController; - @Autowired private SettingsRepository settingsRepository; - @Autowired private MailService mailService; - @Autowired private ReleaseCheckService releaseCheckService; + private final GenericRepository genericRepository; + private final LogController logController; + private final SettingsRepository settingsRepository; + private final MailService mailService; + private final ReleaseCheckService releaseCheckService; // ------------------------------------------------------------------------- // Paths @@ -65,7 +64,7 @@ public class AboutSettingsController { // HTTP methods // ------------------------------------------------------------------------- - @RequestMapping(value = ABOUT_PATH, method = RequestMethod.GET) + @GetMapping(value = ABOUT_PATH) public String getAbout(Model model) { model.addAttribute("version", CONFIG.getSteveVersion()); model.addAttribute("db", genericRepository.getDBVersion()); @@ -77,15 +76,15 @@ public String getAbout(Model model) { return "about"; } - @RequestMapping(value = SETTINGS_PATH, method = RequestMethod.GET) + @GetMapping(value = SETTINGS_PATH) public String getSettings(Model model) { - SettingsForm form = settingsRepository.getForm(); + var form = settingsRepository.getForm(); model.addAttribute("features", NotificationFeature.values()); model.addAttribute("settingsForm", form); return "settings"; } - @RequestMapping(params = "change", value = SETTINGS_PATH, method = RequestMethod.POST) + @PostMapping(params = "change", value = SETTINGS_PATH) public String postSettings(@Valid @ModelAttribute("settingsForm") SettingsForm settingsForm, BindingResult result, Model model) { if (result.hasErrors()) { @@ -97,7 +96,7 @@ public String postSettings(@Valid @ModelAttribute("settingsForm") SettingsForm s return "redirect:/manager/settings"; } - @RequestMapping(params = "testMail", value = SETTINGS_PATH, method = RequestMethod.POST) + @PostMapping(params = "testMail", value = SETTINGS_PATH) public String testMail(@Valid @ModelAttribute("settingsForm") SettingsForm settingsForm, BindingResult result, Model model) { if (result.hasErrors()) { diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/AjaxCallController.java b/src/main/java/de/rwth/idsg/steve/web/controller/AjaxCallController.java index 322cdfe52..6aa21ae56 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/AjaxCallController.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/AjaxCallController.java @@ -21,17 +21,14 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import de.rwth.idsg.steve.repository.ChargePointRepository; -import de.rwth.idsg.steve.repository.ReservationRepository; import de.rwth.idsg.steve.repository.TransactionRepository; +import de.rwth.idsg.steve.service.ChargePointsService; +import de.rwth.idsg.steve.service.ReservationsService; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.*; import jakarta.annotation.PostConstruct; import jakarta.servlet.http.HttpServletResponse; @@ -47,13 +44,13 @@ @ResponseBody @RequestMapping( value = "/manager/ajax/{chargeBoxId}", - method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) +@RequiredArgsConstructor public class AjaxCallController { - @Autowired private ChargePointRepository chargePointRepository; - @Autowired private TransactionRepository transactionRepository; - @Autowired private ReservationRepository reservationRepository; + private final ChargePointsService chargePointsService; + private final TransactionRepository transactionRepository; + private final ReservationsService reservationsService; private ObjectMapper objectMapper; @@ -75,24 +72,24 @@ private void init() { // HTTP methods // ------------------------------------------------------------------------- - @RequestMapping(value = CONNECTOR_IDS_PATH) + @GetMapping(value = CONNECTOR_IDS_PATH) public void getConnectorIds(@PathVariable("chargeBoxId") String chargeBoxId, HttpServletResponse response) throws IOException { - String s = serializeArray(chargePointRepository.getNonZeroConnectorIds(chargeBoxId)); + var s = serializeArray(chargePointsService.getNonZeroConnectorIds(chargeBoxId)); writeOutput(response, s); } - @RequestMapping(value = TRANSACTION_IDS_PATH) + @GetMapping(value = TRANSACTION_IDS_PATH) public void getTransactionIds(@PathVariable("chargeBoxId") String chargeBoxId, HttpServletResponse response) throws IOException { - String s = serializeArray(transactionRepository.getActiveTransactionIds(chargeBoxId)); + var s = serializeArray(transactionRepository.getActiveTransactionIds(chargeBoxId)); writeOutput(response, s); } - @RequestMapping(value = RESERVATION_IDS_PATH) + @GetMapping(value = RESERVATION_IDS_PATH) public void getReservationIds(@PathVariable("chargeBoxId") String chargeBoxId, HttpServletResponse response) throws IOException { - String s = serializeArray(reservationRepository.getActiveReservationIds(chargeBoxId)); + var s = serializeArray(reservationsService.getActiveReservationIds(chargeBoxId)); writeOutput(response, s); } @@ -113,9 +110,8 @@ private String serializeArray(List list) { * * That's why we are directly accessing the low-level HttpServletResponse and manually writing to output. */ - private void writeOutput(HttpServletResponse response, String str) throws IOException { + private static void writeOutput(HttpServletResponse response, String str) throws IOException { response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.getWriter().write(str); } - } diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/ChargePointsController.java b/src/main/java/de/rwth/idsg/steve/web/controller/ChargePointsController.java index b74b7aef4..020bbfe7e 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/ChargePointsController.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/ChargePointsController.java @@ -19,23 +19,19 @@ package de.rwth.idsg.steve.web.controller; import de.rwth.idsg.steve.ocpp.OcppProtocol; -import de.rwth.idsg.steve.repository.ChargePointRepository; -import de.rwth.idsg.steve.repository.dto.ChargePoint; import de.rwth.idsg.steve.service.ChargePointHelperService; +import de.rwth.idsg.steve.service.ChargePointsService; import de.rwth.idsg.steve.utils.ControllerHelper; import de.rwth.idsg.steve.utils.mapper.ChargePointDetailsMapper; import de.rwth.idsg.steve.web.dto.ChargePointBatchInsertForm; import de.rwth.idsg.steve.web.dto.ChargePointForm; import de.rwth.idsg.steve.web.dto.ChargePointQueryForm; import jooq.steve.db.tables.records.ChargeBoxRecord; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.*; import jakarta.validation.Valid; import java.util.Arrays; @@ -44,18 +40,17 @@ import java.util.stream.Collectors; /** - * * @author Sevket Goekay - * */ @Controller @RequestMapping(value = "/manager/chargepoints") +@RequiredArgsConstructor public class ChargePointsController { - @Autowired protected ChargePointRepository chargePointRepository; - @Autowired protected ChargePointHelperService chargePointHelperService; + private final ChargePointsService chargePointsService; + private final ChargePointHelperService chargePointHelperService; - protected static final String PARAMS = "params"; + private static final String PARAMS = "params"; private static final List upToOcpp15RegistrationStatusList = Arrays.stream(ocpp.cs._2012._06.RegistrationStatus.values()) .map(ocpp.cs._2012._06.RegistrationStatus::value) @@ -69,33 +64,33 @@ public class ChargePointsController { // Paths // ------------------------------------------------------------------------- - protected static final String QUERY_PATH = "/query"; + private static final String QUERY_PATH = "/query"; - protected static final String DETAILS_PATH = "/details/{chargeBoxPk}"; - protected static final String DELETE_PATH = "/delete/{chargeBoxPk}"; - protected static final String UPDATE_PATH = "/update"; - protected static final String ADD_PATH = "/add"; + private static final String DETAILS_PATH = "/details/{chargeBoxPk}"; + private static final String DELETE_PATH = "/delete/{chargeBoxPk}"; + private static final String UPDATE_PATH = "/update"; + private static final String ADD_PATH = "/add"; - protected static final String ADD_SINGLE_PATH = "/add/single"; - protected static final String ADD_BATCH_PATH = "/add/batch"; + private static final String ADD_SINGLE_PATH = "/add/single"; + private static final String ADD_BATCH_PATH = "/add/batch"; // We need the slash at the end to support chargeBoxIds with dots etc. in them // Issue: https://github.com/steve-community/steve/issues/270 // Solution: https://stackoverflow.com/a/18378817 - protected static final String UNKNOWN_REMOVE_PATH = "/unknown/remove/{chargeBoxId}/"; - protected static final String UNKNOWN_ADD_PATH = "/unknown/add/{chargeBoxId}/"; + private static final String UNKNOWN_REMOVE_PATH = "/unknown/remove/{chargeBoxId}/"; + private static final String UNKNOWN_ADD_PATH = "/unknown/add/{chargeBoxId}/"; // ------------------------------------------------------------------------- // HTTP methods // ------------------------------------------------------------------------- - @RequestMapping(method = RequestMethod.GET) + @GetMapping public String getOverview(Model model) { initList(model, new ChargePointQueryForm()); return "data-man/chargepoints"; } - @RequestMapping(value = QUERY_PATH, method = RequestMethod.GET) + @GetMapping(value = QUERY_PATH) public String getQuery(@ModelAttribute(PARAMS) ChargePointQueryForm params, Model model) { initList(model, params); return "data-man/chargepoints"; @@ -103,14 +98,14 @@ public String getQuery(@ModelAttribute(PARAMS) ChargePointQueryForm params, Mode private void initList(Model model, ChargePointQueryForm params) { model.addAttribute(PARAMS, params); - model.addAttribute("cpList", chargePointRepository.getOverview(params)); + model.addAttribute("cpList", chargePointsService.getOverview(params)); model.addAttribute("unknownList", chargePointHelperService.getUnknownChargePoints()); } - @RequestMapping(value = DETAILS_PATH, method = RequestMethod.GET) + @GetMapping(value = DETAILS_PATH) public String getDetails(@PathVariable("chargeBoxPk") int chargeBoxPk, Model model) { - ChargePoint.Details cp = chargePointRepository.getDetails(chargeBoxPk); - ChargePointForm form = ChargePointDetailsMapper.mapToForm(cp); + var cp = chargePointsService.getDetails(chargeBoxPk); + var form = ChargePointDetailsMapper.mapToForm(cp); model.addAttribute("chargePointForm", form); model.addAttribute("cp", cp); @@ -120,12 +115,12 @@ public String getDetails(@PathVariable("chargeBoxPk") int chargeBoxPk, Model mod return "data-man/chargepointDetails"; } - private List getRegistrationStatusList(ChargeBoxRecord chargeBoxRecord) { + private static List getRegistrationStatusList(ChargeBoxRecord chargeBoxRecord) { if (chargeBoxRecord.getOcppProtocol() == null) { return upToOcpp15RegistrationStatusList; } - OcppProtocol protocol = OcppProtocol.fromCompositeValue(chargeBoxRecord.getOcppProtocol()); + var protocol = OcppProtocol.fromCompositeValue(chargeBoxRecord.getOcppProtocol()); switch (protocol.getVersion()) { case V_12: case V_15: @@ -137,14 +132,14 @@ private List getRegistrationStatusList(ChargeBoxRecord chargeBoxRecord) } } - @RequestMapping(value = ADD_PATH, method = RequestMethod.GET) + @GetMapping(value = ADD_PATH) public String addGet(Model model) { model.addAttribute("chargePointForm", new ChargePointForm()); setCommonAttributesForSingleAdd(model); return "data-man/chargepointAdd"; } - @RequestMapping(params = "add", value = ADD_SINGLE_PATH, method = RequestMethod.POST) + @GetMapping(params = "add", value = ADD_SINGLE_PATH) public String addSinglePost(@Valid @ModelAttribute("chargePointForm") ChargePointForm chargePointForm, BindingResult result, Model model) { if (result.hasErrors()) { @@ -156,7 +151,7 @@ public String addSinglePost(@Valid @ModelAttribute("chargePointForm") ChargePoin return toOverview(); } - @RequestMapping(value = ADD_BATCH_PATH, method = RequestMethod.POST) + @PostMapping(value = ADD_BATCH_PATH) public String addBatchPost(@Valid @ModelAttribute("batchChargePointForm") ChargePointBatchInsertForm form, BindingResult result, Model model) { if (result.hasErrors()) { @@ -169,7 +164,7 @@ public String addBatchPost(@Valid @ModelAttribute("batchChargePointForm") Charge return toOverview(); } - @RequestMapping(params = "update", value = UPDATE_PATH, method = RequestMethod.POST) + @PostMapping(params = "update", value = UPDATE_PATH) public String update(@Valid @ModelAttribute("chargePointForm") ChargePointForm chargePointForm, BindingResult result, Model model) { if (result.hasErrors()) { @@ -177,29 +172,29 @@ public String update(@Valid @ModelAttribute("chargePointForm") ChargePointForm c return "data-man/chargepointDetails"; } - chargePointRepository.updateChargePoint(chargePointForm); + chargePointsService.updateChargePoint(chargePointForm); return toOverview(); } - @RequestMapping(value = DELETE_PATH, method = RequestMethod.POST) + @PostMapping(value = DELETE_PATH) public String delete(@PathVariable("chargeBoxPk") int chargeBoxPk) { - chargePointRepository.deleteChargePoint(chargeBoxPk); + chargePointsService.deleteChargePoint(chargeBoxPk); return toOverview(); } - @RequestMapping(value = UNKNOWN_ADD_PATH, method = RequestMethod.POST) + @PostMapping(value = UNKNOWN_ADD_PATH) public String addUnknownChargeBoxId(@PathVariable("chargeBoxId") String chargeBoxId) { add(Collections.singletonList(chargeBoxId)); return toOverview(); } - @RequestMapping(value = UNKNOWN_REMOVE_PATH, method = RequestMethod.POST) + @PostMapping(value = UNKNOWN_REMOVE_PATH) public String removeUnknownChargeBoxId(@PathVariable("chargeBoxId") String chargeBoxId) { chargePointHelperService.removeUnknown(Collections.singletonList(chargeBoxId)); return toOverview(); } - protected void addCountryCodes(Model model) { + private static void addCountryCodes(Model model) { model.addAttribute("countryCodes", ControllerHelper.COUNTRY_DROPDOWN); } @@ -207,17 +202,17 @@ protected void addCountryCodes(Model model) { // Back to Overview // ------------------------------------------------------------------------- - @RequestMapping(params = "backToOverview", value = ADD_SINGLE_PATH, method = RequestMethod.POST) + @PostMapping(params = "backToOverview", value = ADD_SINGLE_PATH) public String addBackToOverview() { return toOverview(); } - @RequestMapping(params = "backToOverview", value = UPDATE_PATH, method = RequestMethod.POST) + @PostMapping(params = "backToOverview", value = UPDATE_PATH) public String updateBackToOverview() { return toOverview(); } - protected String toOverview() { + private static String toOverview() { return "redirect:/manager/chargepoints"; } @@ -225,7 +220,7 @@ protected String toOverview() { // Helpers // ------------------------------------------------------------------------- - private void setCommonAttributesForSingleAdd(Model model) { + private static void setCommonAttributesForSingleAdd(Model model) { addCountryCodes(model); model.addAttribute("batchChargePointForm", new ChargePointBatchInsertForm()); // we don't know the protocol yet. but, a list with only "accepted" and "rejected" is a good starting point. @@ -233,12 +228,12 @@ private void setCommonAttributesForSingleAdd(Model model) { } private void add(ChargePointForm form) { - chargePointRepository.addChargePoint(form); + chargePointsService.addChargePoint(form); chargePointHelperService.removeUnknown(Collections.singletonList(form.getChargeBoxId())); } private void add(List idList) { - chargePointRepository.addChargePointList(idList); + chargePointsService.addChargePointList(idList); chargePointHelperService.removeUnknown(idList); } } diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/ChargingProfilesController.java b/src/main/java/de/rwth/idsg/steve/web/controller/ChargingProfilesController.java index ff8ee3971..ea6059f1e 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/ChargingProfilesController.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/ChargingProfilesController.java @@ -18,21 +18,17 @@ */ package de.rwth.idsg.steve.web.controller; -import de.rwth.idsg.steve.repository.ChargePointRepository; import de.rwth.idsg.steve.repository.ChargingProfileRepository; -import de.rwth.idsg.steve.repository.dto.ChargingProfile; +import de.rwth.idsg.steve.service.ChargePointsService; import de.rwth.idsg.steve.utils.mapper.ChargingProfileDetailsMapper; import de.rwth.idsg.steve.web.dto.ChargingProfileAssignmentQueryForm; import de.rwth.idsg.steve.web.dto.ChargingProfileForm; import de.rwth.idsg.steve.web.dto.ChargingProfileQueryForm; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.*; import jakarta.validation.Valid; @@ -42,10 +38,11 @@ */ @Controller @RequestMapping(value = "/manager/chargingProfiles") +@RequiredArgsConstructor public class ChargingProfilesController { - @Autowired private ChargePointRepository chargePointRepository; - @Autowired private ChargingProfileRepository repository; + private final ChargePointsService chargePointsService; + private final ChargingProfileRepository repository; private static final String PARAMS = "params"; @@ -66,27 +63,27 @@ public class ChargingProfilesController { // HTTP methods // ------------------------------------------------------------------------- - @RequestMapping(method = RequestMethod.GET) + @GetMapping public String getOverview(Model model) { - ChargingProfileQueryForm queryForm = new ChargingProfileQueryForm(); + var queryForm = new ChargingProfileQueryForm(); model.addAttribute(PARAMS, queryForm); initList(queryForm, model); return "data-man/chargingProfiles"; } - @RequestMapping(value = QUERY_PATH, method = RequestMethod.GET) + @GetMapping(value = QUERY_PATH) public String getQuery(@ModelAttribute(PARAMS) ChargingProfileQueryForm queryForm, Model model) { initList(queryForm, model); return "data-man/chargingProfiles"; } - @RequestMapping(value = ADD_PATH, method = RequestMethod.GET) + @GetMapping(value = ADD_PATH) public String addGet(Model model) { model.addAttribute("form", new ChargingProfileForm()); return "data-man/chargingProfileAdd"; } - @RequestMapping(params = "add", value = ADD_PATH, method = RequestMethod.POST) + @PostMapping(params = "add", value = ADD_PATH) public String addPost(@Valid @ModelAttribute("form") ChargingProfileForm form, BindingResult result, Model model) { if (result.hasErrors()) { @@ -97,7 +94,7 @@ public String addPost(@Valid @ModelAttribute("form") ChargingProfileForm form, return toOverview(); } - @RequestMapping(params = "update", value = UPDATE_PATH, method = RequestMethod.POST) + @PostMapping(params = "update", value = UPDATE_PATH) public String update(@Valid @ModelAttribute("form") ChargingProfileForm form, BindingResult result, Model model) { if (result.hasErrors()) { @@ -108,36 +105,36 @@ public String update(@Valid @ModelAttribute("form") ChargingProfileForm form, return toOverview(); } - @RequestMapping(params = "backToOverview", value = ADD_PATH, method = RequestMethod.POST) + @PostMapping(params = "backToOverview", value = ADD_PATH) public String addBackToOverview() { return toOverview(); } - @RequestMapping(params = "backToOverview", value = UPDATE_PATH, method = RequestMethod.POST) + @PostMapping(params = "backToOverview", value = UPDATE_PATH) public String updateBackToOverview() { return toOverview(); } - @RequestMapping(value = DELETE_PATH, method = RequestMethod.POST) + @PostMapping(value = DELETE_PATH) public String delete(@PathVariable("chargingProfilePk") int chargingProfilePk) { repository.delete(chargingProfilePk); return toOverview(); } - @RequestMapping(value = DETAILS_PATH, method = RequestMethod.GET) + @GetMapping(value = DETAILS_PATH) public String getDetails(@PathVariable("chargingProfilePk") int chargingProfilePk, Model model) { - ChargingProfile.Details details = repository.getDetails(chargingProfilePk); - ChargingProfileForm form = ChargingProfileDetailsMapper.mapToForm(details); + var details = repository.getDetails(chargingProfilePk); + var form = ChargingProfileDetailsMapper.mapToForm(details); model.addAttribute("form", form); return "data-man/chargingProfileDetails"; } - @RequestMapping(value = ASSIGNMENTS_PATH, method = RequestMethod.GET) + @GetMapping(value = ASSIGNMENTS_PATH) public String getAssignments(@ModelAttribute(PARAMS) ChargingProfileAssignmentQueryForm form, Model model) { model.addAttribute(PARAMS, form); model.addAttribute("profileList", repository.getBasicInfo()); - model.addAttribute("cpList", chargePointRepository.getChargeBoxIds()); + model.addAttribute("cpList", chargePointsService.getChargeBoxIds()); model.addAttribute("assignments", repository.getAssignments(form)); return "data-man/chargingProfileAssignments"; } @@ -146,7 +143,7 @@ private void initList(ChargingProfileQueryForm queryForm, Model model) { model.addAttribute("profileList", repository.getOverview(queryForm)); } - private String toOverview() { + private static String toOverview() { return "redirect:/manager/chargingProfiles"; } } diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/HomeController.java b/src/main/java/de/rwth/idsg/steve/web/controller/HomeController.java index 39e2ed457..788e8940a 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/HomeController.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/HomeController.java @@ -24,26 +24,25 @@ import de.rwth.idsg.steve.utils.ConnectorStatusCountFilter; import de.rwth.idsg.steve.utils.ConnectorStatusFilter; import de.rwth.idsg.steve.web.dto.ConnectorStatusForm; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; import java.util.List; /** - * * @author Sevket Goekay - * */ @Controller -@RequestMapping(value = "/manager", method = RequestMethod.GET) +@RequestMapping(value = "/manager") +@RequiredArgsConstructor public class HomeController { - @Autowired private ChargePointRepository chargePointRepository; - @Autowired private ChargePointHelperService chargePointHelperService; + private final ChargePointRepository chargePointRepository; + private final ChargePointHelperService chargePointHelperService; private static final String PARAMS = "params"; @@ -60,24 +59,24 @@ public class HomeController { // HTTP methods // ------------------------------------------------------------------------- - @RequestMapping(value = {"", HOME_PREFIX}) + @GetMapping(value = {"", HOME_PREFIX}) public String getHome(Model model) { model.addAttribute("stats", chargePointHelperService.getStats()); return "home"; } - @RequestMapping(value = CONNECTOR_STATUS_PATH) + @GetMapping(value = CONNECTOR_STATUS_PATH) public String getConnectorStatus(Model model) { return getConnectorStatusQuery(new ConnectorStatusForm(), model); } - @RequestMapping(value = CONNECTOR_STATUS_QUERY_PATH) + @GetMapping(value = CONNECTOR_STATUS_QUERY_PATH) public String getConnectorStatusQuery(@ModelAttribute(PARAMS) ConnectorStatusForm params, Model model) { model.addAttribute("cpList", chargePointRepository.getChargeBoxIds()); model.addAttribute("statusValues", ConnectorStatusCountFilter.ALL_STATUS_VALUES); model.addAttribute(PARAMS, params); - List latestList = chargePointHelperService.getChargePointConnectorStatus(params); + var latestList = chargePointHelperService.getChargePointConnectorStatus(params); List filteredList; if (params.getStrategy() == ConnectorStatusForm.Strategy.PreferZero) { filteredList = ConnectorStatusFilter.filterAndPreferZero(latestList); @@ -88,7 +87,7 @@ public String getConnectorStatusQuery(@ModelAttribute(PARAMS) ConnectorStatusFor return "connectorStatus"; } - @RequestMapping(value = OCPP_JSON_STATUS) + @GetMapping(value = OCPP_JSON_STATUS) public String getOcppJsonStatus(Model model) { model.addAttribute("ocppJsonStatusList", chargePointHelperService.getOcppJsonStatus()); return "ocppJsonStatus"; diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/LogController.java b/src/main/java/de/rwth/idsg/steve/web/controller/LogController.java index ba51be2bf..fe3161282 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/LogController.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/LogController.java @@ -21,8 +21,8 @@ import de.rwth.idsg.steve.utils.LogFileRetriever; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @@ -41,7 +41,7 @@ @RequestMapping(value = "/manager") public class LogController { - @RequestMapping(value = "/log", method = RequestMethod.GET) + @GetMapping(value = "/log") public void log(HttpServletResponse response) { response.setContentType("text/plain"); @@ -61,5 +61,4 @@ public void log(HttpServletResponse response) { public String getLogFilePath() { return LogFileRetriever.INSTANCE.getLogFilePathOrErrorMessage(); } - } diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/NoAccessController.java b/src/main/java/de/rwth/idsg/steve/web/controller/NoAccessController.java index 3fa4481bb..098efd3e8 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/NoAccessController.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/NoAccessController.java @@ -38,5 +38,4 @@ public class NoAccessController { public String accessDenied() { return "noAccess"; } - } diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/Ocpp12Controller.java b/src/main/java/de/rwth/idsg/steve/web/controller/Ocpp12Controller.java index 1ef1a134f..811ad012a 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/Ocpp12Controller.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/Ocpp12Controller.java @@ -21,7 +21,7 @@ import de.rwth.idsg.steve.ocpp.OcppVersion; import de.rwth.idsg.steve.service.ChargePointHelperService; import de.rwth.idsg.steve.service.ChargePointServiceClient; -import de.rwth.idsg.steve.service.OcppTagService; +import de.rwth.idsg.steve.service.OcppTagsService; import de.rwth.idsg.steve.web.dto.ocpp.ChangeAvailabilityParams; import de.rwth.idsg.steve.web.dto.ocpp.ChangeConfigurationParams; import de.rwth.idsg.steve.web.dto.ocpp.ConfigurationKey; @@ -33,13 +33,11 @@ import de.rwth.idsg.steve.web.dto.ocpp.ResetParams; import de.rwth.idsg.steve.web.dto.ocpp.UnlockConnectorParams; import de.rwth.idsg.steve.web.dto.ocpp.UpdateFirmwareParams; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.*; import jakarta.validation.Valid; @@ -53,11 +51,12 @@ */ @Controller @RequestMapping(value = "/manager/operations/v1.2") +@RequiredArgsConstructor public class Ocpp12Controller { - @Autowired protected ChargePointHelperService chargePointHelperService; - @Autowired protected OcppTagService ocppTagService; - @Autowired protected ChargePointServiceClient chargePointServiceClient; + protected final ChargePointHelperService chargePointHelperService; + protected final OcppTagsService ocppTagsService; + protected final ChargePointServiceClient chargePointServiceClient; protected static final String PARAMS = "params"; @@ -104,26 +103,26 @@ protected String getPrefix() { } protected void setActiveUserIdTagList(Model model) { - model.addAttribute("idTagList", ocppTagService.getActiveIdTags()); + model.addAttribute("idTagList", ocppTagsService.getActiveIdTags()); } // ------------------------------------------------------------------------- // Http methods (GET) // ------------------------------------------------------------------------- - @RequestMapping(method = RequestMethod.GET) + @GetMapping public String getBase() { return getRedirectPath(); } - @RequestMapping(value = CHANGE_AVAIL_PATH, method = RequestMethod.GET) + @GetMapping(value = CHANGE_AVAIL_PATH) public String getChangeAvail(Model model) { setCommonAttributes(model); model.addAttribute(PARAMS, new ChangeAvailabilityParams()); return getPrefix() + CHANGE_AVAIL_PATH; } - @RequestMapping(value = CHANGE_CONF_PATH, method = RequestMethod.GET) + @GetMapping(value = CHANGE_CONF_PATH) public String getChangeConf(Model model) { setCommonAttributes(model); model.addAttribute(PARAMS, new ChangeConfigurationParams()); @@ -131,21 +130,21 @@ public String getChangeConf(Model model) { return getPrefix() + CHANGE_CONF_PATH; } - @RequestMapping(value = CLEAR_CACHE_PATH, method = RequestMethod.GET) + @GetMapping(value = CLEAR_CACHE_PATH) public String getClearCache(Model model) { setCommonAttributes(model); model.addAttribute(PARAMS, new MultipleChargePointSelect()); return getPrefix() + CLEAR_CACHE_PATH; } - @RequestMapping(value = GET_DIAG_PATH, method = RequestMethod.GET) + @GetMapping(value = GET_DIAG_PATH) public String getGetDiag(Model model) { setCommonAttributes(model); model.addAttribute(PARAMS, new GetDiagnosticsParams()); return getPrefix() + GET_DIAG_PATH; } - @RequestMapping(value = REMOTE_START_TX_PATH, method = RequestMethod.GET) + @GetMapping(value = REMOTE_START_TX_PATH) public String getRemoteStartTx(Model model) { setCommonAttributesForTx(model); setActiveUserIdTagList(model); @@ -153,28 +152,28 @@ public String getRemoteStartTx(Model model) { return getPrefix() + REMOTE_START_TX_PATH; } - @RequestMapping(value = REMOTE_STOP_TX_PATH, method = RequestMethod.GET) + @GetMapping(value = REMOTE_STOP_TX_PATH) public String getRemoteStopTx(Model model) { setCommonAttributesForTx(model); model.addAttribute(PARAMS, new RemoteStopTransactionParams()); return getPrefix() + REMOTE_STOP_TX_PATH; } - @RequestMapping(value = RESET_PATH, method = RequestMethod.GET) + @GetMapping(value = RESET_PATH) public String getReset(Model model) { setCommonAttributes(model); model.addAttribute(PARAMS, new ResetParams()); return getPrefix() + RESET_PATH; } - @RequestMapping(value = UNLOCK_CON_PATH, method = RequestMethod.GET) + @GetMapping(value = UNLOCK_CON_PATH) public String getUnlockCon(Model model) { setCommonAttributes(model); model.addAttribute(PARAMS, new UnlockConnectorParams()); return getPrefix() + UNLOCK_CON_PATH; } - @RequestMapping(value = UPDATE_FIRM_PATH, method = RequestMethod.GET) + @GetMapping(value = UPDATE_FIRM_PATH) public String getUpdateFirm(Model model) { setCommonAttributes(model); model.addAttribute(PARAMS, new UpdateFirmwareParams()); @@ -185,7 +184,7 @@ public String getUpdateFirm(Model model) { // Http methods (POST) // ------------------------------------------------------------------------- - @RequestMapping(value = CHANGE_AVAIL_PATH, method = RequestMethod.POST) + @PostMapping(value = CHANGE_AVAIL_PATH) public String postChangeAvail(@Valid @ModelAttribute(PARAMS) ChangeAvailabilityParams params, BindingResult result, Model model) { if (result.hasErrors()) { @@ -195,7 +194,7 @@ public String postChangeAvail(@Valid @ModelAttribute(PARAMS) ChangeAvailabilityP return REDIRECT_TASKS_PATH + chargePointServiceClient.changeAvailability(params); } - @RequestMapping(value = CHANGE_CONF_PATH, method = RequestMethod.POST) + @PostMapping(value = CHANGE_CONF_PATH) public String postChangeConf(@Valid @ModelAttribute(PARAMS) ChangeConfigurationParams params, BindingResult result, Model model) { if (result.hasErrors()) { @@ -206,7 +205,7 @@ public String postChangeConf(@Valid @ModelAttribute(PARAMS) ChangeConfigurationP return REDIRECT_TASKS_PATH + chargePointServiceClient.changeConfiguration(params); } - @RequestMapping(value = CLEAR_CACHE_PATH, method = RequestMethod.POST) + @PostMapping(value = CLEAR_CACHE_PATH) public String postClearCache(@Valid @ModelAttribute(PARAMS) MultipleChargePointSelect params, BindingResult result, Model model) { if (result.hasErrors()) { @@ -216,7 +215,7 @@ public String postClearCache(@Valid @ModelAttribute(PARAMS) MultipleChargePointS return REDIRECT_TASKS_PATH + chargePointServiceClient.clearCache(params); } - @RequestMapping(value = GET_DIAG_PATH, method = RequestMethod.POST) + @PostMapping(value = GET_DIAG_PATH) public String postGetDiag(@Valid @ModelAttribute(PARAMS) GetDiagnosticsParams params, BindingResult result, Model model) { if (result.hasErrors()) { @@ -226,7 +225,7 @@ public String postGetDiag(@Valid @ModelAttribute(PARAMS) GetDiagnosticsParams pa return REDIRECT_TASKS_PATH + chargePointServiceClient.getDiagnostics(params); } - @RequestMapping(value = REMOTE_START_TX_PATH, method = RequestMethod.POST) + @PostMapping(value = REMOTE_START_TX_PATH) public String postRemoteStartTx(@Valid @ModelAttribute(PARAMS) RemoteStartTransactionParams params, BindingResult result, Model model) { if (result.hasErrors()) { @@ -237,7 +236,7 @@ public String postRemoteStartTx(@Valid @ModelAttribute(PARAMS) RemoteStartTransa return REDIRECT_TASKS_PATH + chargePointServiceClient.remoteStartTransaction(params); } - @RequestMapping(value = REMOTE_STOP_TX_PATH, method = RequestMethod.POST) + @PostMapping(value = REMOTE_STOP_TX_PATH) public String postRemoteStopTx(@Valid @ModelAttribute(PARAMS) RemoteStopTransactionParams params, BindingResult result, Model model) { if (result.hasErrors()) { @@ -247,7 +246,7 @@ public String postRemoteStopTx(@Valid @ModelAttribute(PARAMS) RemoteStopTransact return REDIRECT_TASKS_PATH + chargePointServiceClient.remoteStopTransaction(params); } - @RequestMapping(value = RESET_PATH, method = RequestMethod.POST) + @PostMapping(value = RESET_PATH) public String postReset(@Valid @ModelAttribute(PARAMS) ResetParams params, BindingResult result, Model model) { if (result.hasErrors()) { @@ -257,7 +256,7 @@ public String postReset(@Valid @ModelAttribute(PARAMS) ResetParams params, return REDIRECT_TASKS_PATH + chargePointServiceClient.reset(params); } - @RequestMapping(value = UNLOCK_CON_PATH, method = RequestMethod.POST) + @PostMapping(value = UNLOCK_CON_PATH) public String postUnlockCon(@Valid @ModelAttribute(PARAMS) UnlockConnectorParams params, BindingResult result, Model model) { if (result.hasErrors()) { @@ -267,7 +266,7 @@ public String postUnlockCon(@Valid @ModelAttribute(PARAMS) UnlockConnectorParams return REDIRECT_TASKS_PATH + chargePointServiceClient.unlockConnector(params); } - @RequestMapping(value = UPDATE_FIRM_PATH, method = RequestMethod.POST) + @PostMapping(value = UPDATE_FIRM_PATH) public String postUpdateFirm(@Valid @ModelAttribute(PARAMS) UpdateFirmwareParams params, BindingResult result, Model model) { if (result.hasErrors()) { diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/Ocpp15Controller.java b/src/main/java/de/rwth/idsg/steve/web/controller/Ocpp15Controller.java index 0bbefc6aa..df34c8c73 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/Ocpp15Controller.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/Ocpp15Controller.java @@ -19,6 +19,9 @@ package de.rwth.idsg.steve.web.controller; import de.rwth.idsg.steve.ocpp.OcppVersion; +import de.rwth.idsg.steve.service.ChargePointHelperService; +import de.rwth.idsg.steve.service.ChargePointServiceClient; +import de.rwth.idsg.steve.service.OcppTagsService; import de.rwth.idsg.steve.web.dto.ocpp.CancelReservationParams; import de.rwth.idsg.steve.web.dto.ocpp.ConfigurationKey; import de.rwth.idsg.steve.web.dto.ocpp.ConfigurationKeyReadWrite; @@ -30,9 +33,7 @@ import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.*; import jakarta.validation.Valid; @@ -59,6 +60,11 @@ public class Ocpp15Controller extends Ocpp12Controller { private static final String GET_LIST_VERSION_PATH = "/GetLocalListVersion"; private static final String SEND_LIST_PATH = "/SendLocalList"; + public Ocpp15Controller(ChargePointHelperService chargePointHelperService, OcppTagsService ocppTagsService, + ChargePointServiceClient chargePointServiceClient) { + super(chargePointHelperService, ocppTagsService, chargePointServiceClient); + } + // ------------------------------------------------------------------------- // Helpers // ------------------------------------------------------------------------- @@ -86,14 +92,14 @@ protected String getPrefix() { } private void setAllUserIdTagList(Model model) { - model.addAttribute("idTagList", ocppTagService.getIdTags()); + model.addAttribute("idTagList", ocppTagsService.getIdTags()); } // ------------------------------------------------------------------------- // Http methods (GET) // ------------------------------------------------------------------------- - @RequestMapping(value = RESERVE_PATH, method = RequestMethod.GET) + @GetMapping(value = RESERVE_PATH) public String getReserveNow(Model model) { setCommonAttributes(model); setActiveUserIdTagList(model); @@ -101,21 +107,21 @@ public String getReserveNow(Model model) { return getPrefix() + RESERVE_PATH; } - @RequestMapping(value = CANCEL_RESERV_PATH, method = RequestMethod.GET) + @GetMapping(value = CANCEL_RESERV_PATH) public String getCancelReserv(Model model) { setCommonAttributes(model); model.addAttribute(PARAMS, new CancelReservationParams()); return getPrefix() + CANCEL_RESERV_PATH; } - @RequestMapping(value = DATA_TRANSFER_PATH, method = RequestMethod.GET) + @GetMapping(value = DATA_TRANSFER_PATH) public String getDataTransfer(Model model) { setCommonAttributes(model); model.addAttribute(PARAMS, new DataTransferParams()); return getPrefix() + DATA_TRANSFER_PATH; } - @RequestMapping(value = GET_CONF_PATH, method = RequestMethod.GET) + @GetMapping(value = GET_CONF_PATH) public String getGetConf(Model model) { setCommonAttributes(model); model.addAttribute(PARAMS, new GetConfigurationParams()); @@ -123,14 +129,14 @@ public String getGetConf(Model model) { return getPrefix() + GET_CONF_PATH; } - @RequestMapping(value = GET_LIST_VERSION_PATH, method = RequestMethod.GET) + @GetMapping(value = GET_LIST_VERSION_PATH) public String getListVersion(Model model) { setCommonAttributes(model); model.addAttribute(PARAMS, new MultipleChargePointSelect()); return getPrefix() + GET_LIST_VERSION_PATH; } - @RequestMapping(value = SEND_LIST_PATH, method = RequestMethod.GET) + @GetMapping(value = SEND_LIST_PATH) public String getSendList(Model model) { setCommonAttributes(model); setAllUserIdTagList(model); @@ -142,7 +148,7 @@ public String getSendList(Model model) { // Http methods (POST) // ------------------------------------------------------------------------- - @RequestMapping(value = RESERVE_PATH, method = RequestMethod.POST) + @PostMapping(value = RESERVE_PATH) public String postReserveNow(@Valid @ModelAttribute(PARAMS) ReserveNowParams params, BindingResult result, Model model) { if (result.hasErrors()) { @@ -153,7 +159,7 @@ public String postReserveNow(@Valid @ModelAttribute(PARAMS) ReserveNowParams par return REDIRECT_TASKS_PATH + chargePointServiceClient.reserveNow(params); } - @RequestMapping(value = CANCEL_RESERV_PATH, method = RequestMethod.POST) + @PostMapping(value = CANCEL_RESERV_PATH) public String postCancelReserv(@Valid @ModelAttribute(PARAMS) CancelReservationParams params, BindingResult result, Model model) { if (result.hasErrors()) { @@ -163,7 +169,7 @@ public String postCancelReserv(@Valid @ModelAttribute(PARAMS) CancelReservationP return REDIRECT_TASKS_PATH + chargePointServiceClient.cancelReservation(params); } - @RequestMapping(value = DATA_TRANSFER_PATH, method = RequestMethod.POST) + @PostMapping(value = DATA_TRANSFER_PATH) public String postDataTransfer(@Valid @ModelAttribute(PARAMS) DataTransferParams params, BindingResult result, Model model) { if (result.hasErrors()) { @@ -173,7 +179,7 @@ public String postDataTransfer(@Valid @ModelAttribute(PARAMS) DataTransferParams return REDIRECT_TASKS_PATH + chargePointServiceClient.dataTransfer(params); } - @RequestMapping(value = GET_CONF_PATH, method = RequestMethod.POST) + @PostMapping(value = GET_CONF_PATH) public String postGetConf(@Valid @ModelAttribute(PARAMS) GetConfigurationParams params, BindingResult result, Model model) { if (result.hasErrors()) { @@ -184,7 +190,7 @@ public String postGetConf(@Valid @ModelAttribute(PARAMS) GetConfigurationParams return REDIRECT_TASKS_PATH + chargePointServiceClient.getConfiguration(params); } - @RequestMapping(value = GET_LIST_VERSION_PATH, method = RequestMethod.POST) + @PostMapping(value = GET_LIST_VERSION_PATH) public String postListVersion(@Valid @ModelAttribute(PARAMS) MultipleChargePointSelect params, BindingResult result, Model model) { if (result.hasErrors()) { @@ -194,7 +200,7 @@ public String postListVersion(@Valid @ModelAttribute(PARAMS) MultipleChargePoint return REDIRECT_TASKS_PATH + chargePointServiceClient.getLocalListVersion(params); } - @RequestMapping(value = SEND_LIST_PATH, method = RequestMethod.POST) + @PostMapping(value = SEND_LIST_PATH) public String postSendList(@Valid @ModelAttribute(PARAMS) SendLocalListParams params, BindingResult result, Model model) { if (result.hasErrors()) { diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/Ocpp16Controller.java b/src/main/java/de/rwth/idsg/steve/web/controller/Ocpp16Controller.java index 644015567..618f51a37 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/Ocpp16Controller.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/Ocpp16Controller.java @@ -20,6 +20,9 @@ import de.rwth.idsg.steve.ocpp.OcppVersion; import de.rwth.idsg.steve.repository.ChargingProfileRepository; +import de.rwth.idsg.steve.service.ChargePointHelperService; +import de.rwth.idsg.steve.service.ChargePointServiceClient; +import de.rwth.idsg.steve.service.OcppTagsService; import de.rwth.idsg.steve.web.dto.ocpp.ChangeConfigurationParams; import de.rwth.idsg.steve.web.dto.ocpp.ClearChargingProfileParams; import de.rwth.idsg.steve.web.dto.ocpp.ConfigurationKey; @@ -29,13 +32,10 @@ import de.rwth.idsg.steve.web.dto.ocpp.SetChargingProfileParams; import de.rwth.idsg.steve.web.dto.ocpp.TriggerMessageParams; import ocpp.cs._2015._10.RegistrationStatus; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.*; import jakarta.validation.Valid; @@ -55,7 +55,7 @@ @RequestMapping(value = "/manager/operations/v1.6") public class Ocpp16Controller extends Ocpp15Controller { - @Autowired private ChargingProfileRepository chargingProfileRepository; + private final ChargingProfileRepository chargingProfileRepository; // ------------------------------------------------------------------------- // Paths @@ -66,7 +66,14 @@ public class Ocpp16Controller extends Ocpp15Controller { private static final String SET_CHARGING_PATH = "/SetChargingProfile"; private static final String TRIGGER_MESSAGE_PATH = "/TriggerMessage"; - // ------------------------------------------------------------------------- + public Ocpp16Controller(ChargePointHelperService chargePointHelperService, OcppTagsService ocppTagsService, + ChargePointServiceClient chargePointServiceClient, + ChargingProfileRepository chargingProfileRepository) { + super(chargePointHelperService, ocppTagsService, chargePointServiceClient); + this.chargingProfileRepository = chargingProfileRepository; + } + + // ------------------------------------------------------------------------- // Helpers // ------------------------------------------------------------------------- @@ -116,7 +123,7 @@ protected String getPrefix() { // Old Http methods with changed logic // ------------------------------------------------------------------------- - @RequestMapping(value = GET_CONF_PATH, method = RequestMethod.GET) + @GetMapping(value = GET_CONF_PATH) public String getGetConf(Model model) { setCommonAttributes(model); model.addAttribute(PARAMS, new GetConfigurationParams()); @@ -124,7 +131,7 @@ public String getGetConf(Model model) { return getPrefix() + GET_CONF_PATH; } - @RequestMapping(value = CHANGE_CONF_PATH, method = RequestMethod.GET) + @GetMapping(value = CHANGE_CONF_PATH) public String getChangeConf(Model model) { setCommonAttributes(model); model.addAttribute(PARAMS, new ChangeConfigurationParams()); @@ -132,7 +139,7 @@ public String getChangeConf(Model model) { return getPrefix() + CHANGE_CONF_PATH; } - @RequestMapping(value = GET_CONF_PATH, method = RequestMethod.POST) + @PostMapping(value = GET_CONF_PATH) public String postGetConf(@Valid @ModelAttribute(PARAMS) GetConfigurationParams params, BindingResult result, Model model) { if (result.hasErrors()) { @@ -147,14 +154,14 @@ public String postGetConf(@Valid @ModelAttribute(PARAMS) GetConfigurationParams // New Http methods (GET) // ------------------------------------------------------------------------- - @RequestMapping(value = GET_COMPOSITE_PATH, method = RequestMethod.GET) + @GetMapping(value = GET_COMPOSITE_PATH) public String getGetCompositeSchedule(Model model) { setCommonAttributes(model); model.addAttribute(PARAMS, new GetCompositeScheduleParams()); return getPrefix() + GET_COMPOSITE_PATH; } - @RequestMapping(value = CLEAR_CHARGING_PATH, method = RequestMethod.GET) + @GetMapping(value = CLEAR_CHARGING_PATH) public String getClearChargingProfile(Model model) { setCommonAttributes(model); model.addAttribute("profileList", chargingProfileRepository.getBasicInfo()); @@ -162,7 +169,7 @@ public String getClearChargingProfile(Model model) { return getPrefix() + CLEAR_CHARGING_PATH; } - @RequestMapping(value = SET_CHARGING_PATH, method = RequestMethod.GET) + @GetMapping(value = SET_CHARGING_PATH) public String getSetChargingProfile(Model model) { setCommonAttributes(model); model.addAttribute("profileList", chargingProfileRepository.getBasicInfo()); @@ -170,7 +177,7 @@ public String getSetChargingProfile(Model model) { return getPrefix() + SET_CHARGING_PATH; } - @RequestMapping(value = TRIGGER_MESSAGE_PATH, method = RequestMethod.GET) + @GetMapping(value = TRIGGER_MESSAGE_PATH) public String getTriggerMessage(Model model) { setCommonAttributes(model); model.addAttribute(PARAMS, new TriggerMessageParams()); @@ -181,7 +188,7 @@ public String getTriggerMessage(Model model) { // Http methods (POST) // ------------------------------------------------------------------------- - @RequestMapping(value = TRIGGER_MESSAGE_PATH, method = RequestMethod.POST) + @PostMapping(value = TRIGGER_MESSAGE_PATH) public String postTriggerMessage(@Valid @ModelAttribute(PARAMS) TriggerMessageParams params, BindingResult result, Model model) { if (result.hasErrors()) { @@ -191,7 +198,7 @@ public String postTriggerMessage(@Valid @ModelAttribute(PARAMS) TriggerMessagePa return REDIRECT_TASKS_PATH + chargePointServiceClient.triggerMessage(params); } - @RequestMapping(value = SET_CHARGING_PATH, method = RequestMethod.POST) + @PostMapping(value = SET_CHARGING_PATH) public String postSetChargingProfile(@Valid @ModelAttribute(PARAMS) SetChargingProfileParams params, BindingResult result, Model model) { if (result.hasErrors()) { @@ -201,7 +208,7 @@ public String postSetChargingProfile(@Valid @ModelAttribute(PARAMS) SetChargingP return REDIRECT_TASKS_PATH + chargePointServiceClient.setChargingProfile(params); } - @RequestMapping(value = CLEAR_CHARGING_PATH, method = RequestMethod.POST) + @PostMapping(value = CLEAR_CHARGING_PATH) public String postClearChargingProfile(@Valid @ModelAttribute(PARAMS) ClearChargingProfileParams params, BindingResult result, Model model) { if (result.hasErrors()) { @@ -211,7 +218,7 @@ public String postClearChargingProfile(@Valid @ModelAttribute(PARAMS) ClearCharg return REDIRECT_TASKS_PATH + chargePointServiceClient.clearChargingProfile(params); } - @RequestMapping(value = GET_COMPOSITE_PATH, method = RequestMethod.POST) + @PostMapping(value = GET_COMPOSITE_PATH) public String postGetCompositeSchedule(@Valid @ModelAttribute(PARAMS) GetCompositeScheduleParams params, BindingResult result, Model model) { if (result.hasErrors()) { diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/OcppTagsController.java b/src/main/java/de/rwth/idsg/steve/web/controller/OcppTagsController.java index 4b36fb301..7185268da 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/OcppTagsController.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/OcppTagsController.java @@ -18,14 +18,14 @@ */ package de.rwth.idsg.steve.web.controller; -import de.rwth.idsg.steve.service.OcppTagService; +import de.rwth.idsg.steve.service.OcppTagsService; import de.rwth.idsg.steve.utils.ControllerHelper; import de.rwth.idsg.steve.utils.mapper.OcppTagFormMapper; import de.rwth.idsg.steve.web.dto.OcppTagBatchInsertForm; import de.rwth.idsg.steve.web.dto.OcppTagForm; import de.rwth.idsg.steve.web.dto.OcppTagQueryForm; import jooq.steve.db.tables.records.OcppTagActivityRecord; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; @@ -36,7 +36,6 @@ import jakarta.validation.Valid; import java.util.Collections; -import java.util.List; /** * @author Sevket Goekay @@ -44,9 +43,10 @@ */ @Controller @RequestMapping(value = "/manager/ocppTags") +@RequiredArgsConstructor public class OcppTagsController { - @Autowired protected OcppTagService ocppTagService; + private final OcppTagsService ocppTagsService; protected static final String PARAMS = "params"; @@ -85,7 +85,7 @@ public String getQuery(@ModelAttribute(PARAMS) OcppTagQueryForm params, Model mo @RequestMapping(value = DETAILS_PATH, method = RequestMethod.GET) public String getDetails(@PathVariable("ocppTagPk") int ocppTagPk, Model model) { - OcppTagActivityRecord record = ocppTagService.getRecord(ocppTagPk); + OcppTagActivityRecord record = ocppTagsService.getRecord(ocppTagPk); OcppTagForm form = OcppTagFormMapper.toForm(record); model.addAttribute("activeTransactionCount", record.getActiveTransactionCount()); @@ -111,7 +111,7 @@ public String addSinglePost(@Valid @ModelAttribute("ocppTagForm") OcppTagForm oc return "data-man/ocppTagAdd"; } - ocppTagService.addOcppTag(ocppTagForm); + ocppTagsService.addOcppTag(ocppTagForm); return toOverview(); } @@ -124,7 +124,7 @@ public String addBatchPost(@Valid @ModelAttribute("batchInsertForm") OcppTagBatc return "data-man/ocppTagAdd"; } - ocppTagService.addOcppTagList(form.getIdList()); + ocppTagsService.addOcppTagList(form.getIdList()); return toOverview(); } @@ -136,38 +136,38 @@ public String update(@Valid @ModelAttribute("ocppTagForm") OcppTagForm ocppTagFo return "data-man/ocppTagDetails"; } - ocppTagService.updateOcppTag(ocppTagForm); + ocppTagsService.updateOcppTag(ocppTagForm); return toOverview(); } @RequestMapping(value = DELETE_PATH, method = RequestMethod.POST) public String delete(@PathVariable("ocppTagPk") int ocppTagPk) { - ocppTagService.deleteOcppTag(ocppTagPk); + ocppTagsService.deleteOcppTag(ocppTagPk); return toOverview(); } @RequestMapping(value = UNKNOWN_ADD_PATH, method = RequestMethod.POST) public String addUnknownIdTag(@PathVariable("idTag") String idTag) { - ocppTagService.addOcppTagList(Collections.singletonList(idTag)); + ocppTagsService.addOcppTagList(Collections.singletonList(idTag)); return toOverview(); } @RequestMapping(value = UNKNOWN_REMOVE_PATH, method = RequestMethod.POST) public String removeUnknownIdTag(@PathVariable("idTag") String idTag) { - ocppTagService.removeUnknown(Collections.singletonList(idTag)); + ocppTagsService.removeUnknown(Collections.singletonList(idTag)); return toOverview(); } private void initList(Model model, OcppTagQueryForm params) { model.addAttribute(PARAMS, params); - model.addAttribute("idTagList", ocppTagService.getIdTags()); - model.addAttribute("parentIdTagList", ocppTagService.getParentIdTags()); - model.addAttribute("ocppTagList", ocppTagService.getOverview(params)); - model.addAttribute("unknownList", ocppTagService.getUnknownOcppTags()); + model.addAttribute("idTagList", ocppTagsService.getIdTags()); + model.addAttribute("parentIdTagList", ocppTagsService.getParentIdTags()); + model.addAttribute("ocppTagList", ocppTagsService.getOverview(params)); + model.addAttribute("unknownList", ocppTagsService.getUnknownOcppTags()); } protected void setTags(Model model) { - model.addAttribute("idTagList", ControllerHelper.idTagEnhancer(ocppTagService.getIdTags())); + model.addAttribute("idTagList", ControllerHelper.idTagEnhancer(ocppTagsService.getIdTags())); } // ------------------------------------------------------------------------- diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/TransactionsReservationsController.java b/src/main/java/de/rwth/idsg/steve/web/controller/ReservationsController.java similarity index 74% rename from src/main/java/de/rwth/idsg/steve/web/controller/TransactionsReservationsController.java rename to src/main/java/de/rwth/idsg/steve/web/controller/ReservationsController.java index dd08dd917..1776d51b3 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/TransactionsReservationsController.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/ReservationsController.java @@ -18,25 +18,27 @@ */ package de.rwth.idsg.steve.web.controller; -import de.rwth.idsg.steve.repository.ChargePointRepository; -import de.rwth.idsg.steve.repository.ReservationRepository; import de.rwth.idsg.steve.repository.ReservationStatus; import de.rwth.idsg.steve.repository.TransactionRepository; -import de.rwth.idsg.steve.service.OcppTagService; +import de.rwth.idsg.steve.service.ChargePointsService; +import de.rwth.idsg.steve.service.OcppTagsService; +import de.rwth.idsg.steve.service.ReservationsService; import de.rwth.idsg.steve.service.TransactionStopService; import de.rwth.idsg.steve.web.dto.ReservationQueryForm; import de.rwth.idsg.steve.web.dto.TransactionQueryForm; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; + +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; import java.io.IOException; /** @@ -46,14 +48,15 @@ * @since 15.08.2014 */ @Controller -@RequestMapping(value = "/manager", method = RequestMethod.GET) -public class TransactionsReservationsController { +@RequestMapping(value = "/manager") +@RequiredArgsConstructor +public class ReservationsController { - @Autowired private TransactionRepository transactionRepository; - @Autowired private ReservationRepository reservationRepository; - @Autowired private ChargePointRepository chargePointRepository; - @Autowired private OcppTagService ocppTagService; - @Autowired private TransactionStopService transactionStopService; + private final TransactionRepository transactionRepository; + private final ReservationsService reservationsService; + private final ChargePointsService chargePointsService; + private final OcppTagsService ocppTagsService; + private final TransactionStopService transactionStopService; private static final String PARAMS = "params"; @@ -72,9 +75,9 @@ public class TransactionsReservationsController { // HTTP methods // ------------------------------------------------------------------------- - @RequestMapping(value = TRANSACTIONS_PATH) + @GetMapping(value = TRANSACTIONS_PATH) public String getTransactions(Model model) { - TransactionQueryForm params = new TransactionQueryForm(); + var params = new TransactionQueryForm(); initList(model); model.addAttribute("transList", transactionRepository.getTransactions(params)); @@ -82,19 +85,19 @@ public String getTransactions(Model model) { return "data-man/transactions"; } - @RequestMapping(value = TRANSACTION_STOP_PATH, method = RequestMethod.POST) + @PostMapping(value = TRANSACTION_STOP_PATH) public String stopTransaction(@PathVariable("transactionPk") int transactionPk) { transactionStopService.stop(transactionPk); return "redirect:/manager/transactions"; } - @RequestMapping(value = TRANSACTIONS_DETAILS_PATH) + @GetMapping(value = TRANSACTIONS_DETAILS_PATH) public String getTransactionDetails(@PathVariable("transactionPk") int transactionPk, Model model) { model.addAttribute("details", transactionRepository.getDetails(transactionPk)); return "data-man/transactionDetails"; } - @RequestMapping(value = TRANSACTIONS_QUERY_PATH) + @GetMapping(value = TRANSACTIONS_QUERY_PATH) public String getTransactionsQuery(@Valid @ModelAttribute(PARAMS) TransactionQueryForm params, BindingResult result, Model model, HttpServletResponse response) throws IOException { @@ -105,9 +108,9 @@ public String getTransactionsQuery(@Valid @ModelAttribute(PARAMS) TransactionQue } if (params.isReturnCSV()) { - String fileName = "transactions.csv"; - String headerKey = "Content-Disposition"; - String headerValue = String.format("attachment; filename=\"%s\"", fileName); + var fileName = "transactions.csv"; + var headerKey = "Content-Disposition"; + var headerValue = String.format("attachment; filename=\"%s\"", fileName); response.setContentType("text/csv"); response.setHeader(headerKey, headerValue); transactionRepository.writeTransactionsCSV(params, response.getWriter()); @@ -121,21 +124,21 @@ public String getTransactionsQuery(@Valid @ModelAttribute(PARAMS) TransactionQue } } - @RequestMapping(value = RESERVATIONS_PATH) + @GetMapping(value = RESERVATIONS_PATH) public String getReservations(Model model) { - ReservationQueryForm params = new ReservationQueryForm(); + var params = new ReservationQueryForm(); initResList(model); - model.addAttribute("reservList", reservationRepository.getReservations(params)); + model.addAttribute("reservList", reservationsService.getReservations(params)); model.addAttribute(PARAMS, params); return "data-man/reservations"; } - @RequestMapping(value = RESERVATIONS_QUERY_PATH) + @GetMapping(value = RESERVATIONS_QUERY_PATH) public String getReservationsQuery(@Valid @ModelAttribute(PARAMS) ReservationQueryForm params, BindingResult result, Model model) throws IOException { if (!result.hasErrors()) { - model.addAttribute("reservList", reservationRepository.getReservations(params)); + model.addAttribute("reservList", reservationsService.getReservations(params)); } initResList(model); @@ -144,13 +147,12 @@ public String getReservationsQuery(@Valid @ModelAttribute(PARAMS) ReservationQue } private void initList(Model model) { - model.addAttribute("cpList", chargePointRepository.getChargeBoxIds()); - model.addAttribute("idTagList", ocppTagService.getIdTags()); + model.addAttribute("cpList", chargePointsService.getChargeBoxIds()); + model.addAttribute("idTagList", ocppTagsService.getIdTags()); } private void initResList(Model model) { initList(model); model.addAttribute("statusList", ReservationStatus.getValues()); } - } diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/SignOutController.java b/src/main/java/de/rwth/idsg/steve/web/controller/SignOutController.java index e73ae53ff..741b1fd81 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/SignOutController.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/SignOutController.java @@ -22,8 +22,8 @@ import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices; import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -36,7 +36,7 @@ @RequestMapping(value = "/manager") public class SignOutController { - @RequestMapping(value = "/signout", method = RequestMethod.GET) + @GetMapping(value = "/signout") public String signOut(HttpServletRequest request, HttpServletResponse response) { new SecurityContextLogoutHandler() .logout(request, response, null); diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/TaskController.java b/src/main/java/de/rwth/idsg/steve/web/controller/TasksController.java similarity index 80% rename from src/main/java/de/rwth/idsg/steve/web/controller/TaskController.java rename to src/main/java/de/rwth/idsg/steve/web/controller/TasksController.java index c13690a53..67e10acb7 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/TaskController.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/TasksController.java @@ -24,13 +24,11 @@ import de.rwth.idsg.steve.ocpp.task.GetCompositeScheduleTask; import de.rwth.idsg.steve.ocpp.task.GetConfigurationTask; import de.rwth.idsg.steve.repository.TaskStore; +import lombok.RequiredArgsConstructor; import ocpp.cp._2015._10.GetCompositeScheduleResponse; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.*; /** * @author Sevket Goekay @@ -38,9 +36,10 @@ */ @Controller @RequestMapping(value = "/manager/operations/tasks") -public class TaskController { +@RequiredArgsConstructor +public class TasksController { - @Autowired private TaskStore taskStore; + private final TaskStore taskStore; // ------------------------------------------------------------------------- // Paths @@ -53,38 +52,38 @@ public class TaskController { // HTTP methods // ------------------------------------------------------------------------- - @RequestMapping(method = RequestMethod.GET) + @GetMapping public String getOverview(Model model) { model.addAttribute("taskList", taskStore.getOverview()); return "tasks"; } - @RequestMapping(params = "finished", method = RequestMethod.POST) + @PostMapping(params = "finished") public String clearFinished(Model model) { taskStore.clearFinished(); return getOverview(model); } - @RequestMapping(params = "unfinished", method = RequestMethod.POST) + @PostMapping(params = "unfinished") public String clearUnfinished(Model model) { taskStore.clearUnfinished(); return getOverview(model); } - @RequestMapping(value = TASK_ID_PATH, method = RequestMethod.GET) + @GetMapping(value = TASK_ID_PATH) public String getTaskDetails(@PathVariable("taskId") Integer taskId, Model model) { - CommunicationTask r = taskStore.get(taskId); + var r = taskStore.get(taskId); model.addAttribute("taskId", taskId); model.addAttribute("task", r); return "taskResult"; } - @RequestMapping(value = TASK_DETAILS_PATH, method = RequestMethod.GET) + @GetMapping(value = TASK_DETAILS_PATH) public String getDetailsForChargeBox(@PathVariable("taskId") Integer taskId, @PathVariable("chargeBoxId") String chargeBoxId, Model model) { - CommunicationTask r = taskStore.get(taskId); + var r = taskStore.get(taskId); if (r instanceof GetCompositeScheduleTask) { return processForGetCompositeScheduleTask((GetCompositeScheduleTask) r, chargeBoxId, model); @@ -95,7 +94,7 @@ public String getDetailsForChargeBox(@PathVariable("taskId") Integer taskId, } } - private String processForGetCompositeScheduleTask(GetCompositeScheduleTask k, String chargeBoxId, Model model) { + private static String processForGetCompositeScheduleTask(GetCompositeScheduleTask k, String chargeBoxId, Model model) { RequestResult result = extractResult(k, chargeBoxId); GetCompositeScheduleResponse response = result.getDetails(); @@ -104,7 +103,7 @@ private String processForGetCompositeScheduleTask(GetCompositeScheduleTask k, St return "op16/GetCompositeScheduleResponse"; } - private String processForGetConfigurationTask(GetConfigurationTask k, String chargeBoxId, Model model) { + private static String processForGetConfigurationTask(GetConfigurationTask k, String chargeBoxId, Model model) { RequestResult result = extractResult(k, chargeBoxId); GetConfigurationTask.ResponseWrapper response = result.getDetails(); diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/UsersController.java b/src/main/java/de/rwth/idsg/steve/web/controller/UsersController.java index df720bde4..3ffeee494 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/UsersController.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/UsersController.java @@ -18,14 +18,13 @@ */ package de.rwth.idsg.steve.web.controller; -import de.rwth.idsg.steve.repository.UserRepository; -import de.rwth.idsg.steve.repository.dto.User; -import de.rwth.idsg.steve.service.OcppTagService; +import de.rwth.idsg.steve.service.OcppTagsService; +import de.rwth.idsg.steve.service.UsersService; import de.rwth.idsg.steve.utils.ControllerHelper; import de.rwth.idsg.steve.utils.mapper.UserFormMapper; import de.rwth.idsg.steve.web.dto.UserForm; import de.rwth.idsg.steve.web.dto.UserQueryForm; -import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; @@ -40,13 +39,13 @@ * @author Sevket Goekay * @since 25.11.2015 */ -@AllArgsConstructor @Controller @RequestMapping(value = "/manager/users") +@RequiredArgsConstructor public class UsersController { - private final OcppTagService ocppTagService; - private final UserRepository userRepository; + private final OcppTagsService ocppTagsService; + private final UsersService usersService; private static final String PARAMS = "params"; @@ -79,12 +78,12 @@ public String getQuery(@ModelAttribute(PARAMS) UserQueryForm params, Model model private void initList(Model model, UserQueryForm params) { model.addAttribute(PARAMS, params); - model.addAttribute("userList", userRepository.getOverview(params)); + model.addAttribute("userList", usersService.getOverview(params)); } @GetMapping(value = DETAILS_PATH) public String getDetails(@PathVariable("userPk") int userPk, Model model) { - var details = userRepository.getDetails(userPk); + var details = usersService.getDetails(userPk); UserForm form = UserFormMapper.toForm(details); model.addAttribute("userForm", form); @@ -107,7 +106,7 @@ public String addPost(@Valid @ModelAttribute("userForm") UserForm userForm, return "data-man/userAdd"; } - userRepository.add(userForm); + usersService.add(userForm); return toOverview(); } @@ -119,18 +118,18 @@ public String update(@Valid @ModelAttribute("userForm") UserForm userForm, return "data-man/userDetails"; } - userRepository.update(userForm); + usersService.update(userForm); return toOverview(); } @PostMapping(value = DELETE_PATH) public String delete(@PathVariable("userPk") int userPk) { - userRepository.delete(userPk); + usersService.delete(userPk); return toOverview(); } private void setTags(Model model, List idTagsFromUser) { - var fromDB = ocppTagService.getIdTagsWithoutUser(); + var fromDB = ocppTagsService.getIdTagsWithoutUser(); // new temp list because we want to have a specific order var idTagList = new ArrayList<>(fromDB.size() + idTagsFromUser.size()); diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/WebUsersController.java b/src/main/java/de/rwth/idsg/steve/web/controller/WebUsersController.java index 7ac10e435..c7aee3e65 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/WebUsersController.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/WebUsersController.java @@ -18,7 +18,7 @@ */ package de.rwth.idsg.steve.web.controller; -import de.rwth.idsg.steve.service.WebUserService; +import de.rwth.idsg.steve.service.WebUsersService; import de.rwth.idsg.steve.web.dto.WebUserAuthority; import de.rwth.idsg.steve.web.dto.WebUserBaseForm; import de.rwth.idsg.steve.web.dto.WebUserForm; @@ -35,12 +35,12 @@ import jakarta.validation.Valid; -@RequiredArgsConstructor @Controller @RequestMapping(value = "/manager/webusers") +@RequiredArgsConstructor public class WebUsersController { - private final WebUserService webUserService; + private final WebUsersService webUsersService; private static final String PARAMS = "params"; @@ -75,12 +75,12 @@ public String getQuery(@ModelAttribute(PARAMS) WebUserQueryForm params, Model mo private void initList(Model model, WebUserQueryForm params) { model.addAttribute(PARAMS, params); - model.addAttribute("webuserList", webUserService.getOverview(params)); + model.addAttribute("webuserList", webUsersService.getOverview(params)); } @GetMapping(value = DETAILS_PATH) public String getDetails(@PathVariable("webUserPk") Integer webUserPk, Model model) { - WebUserBaseForm form = webUserService.getDetails(webUserPk); + var form = webUsersService.getDetails(webUserPk); model.addAttribute("webuserForm", form); model.addAttribute("availableAuthorities", WebUserAuthority.values()); @@ -89,7 +89,7 @@ public String getDetails(@PathVariable("webUserPk") Integer webUserPk, Model mod @GetMapping(value = ADD_PATH) public String addGet(Model model) { - WebUserForm webUserForm = new WebUserForm(); + var webUserForm = new WebUserForm(); webUserForm.setAuthorities(WebUserAuthority.USER); model.addAttribute("webuserForm", webUserForm); model.addAttribute("availableAuthorities", WebUserAuthority.values()); @@ -104,7 +104,7 @@ public String addPost(@Valid @ModelAttribute("webuserForm") WebUserForm webuserF return "data-man/webuserAdd"; } - webUserService.add(webuserForm); + webUsersService.add(webuserForm); return toOverview(); } @@ -116,20 +116,20 @@ public String update(@Valid @ModelAttribute("webuserForm") WebUserBaseForm webus return "data-man/webuserDetails"; } - webUserService.update(webuserBaseForm); + webUsersService.update(webuserBaseForm); return toOverview(); } @GetMapping(value = PASSWORD_PATH) public String passwordChangeGet(@PathVariable("webUserName") String webUserName, Model model) { - WebUserBaseForm base = webUserService.getDetails(webUserName); + var base = webUsersService.getDetails(webUserName); WebUserForm webUserForm = fromBase(base); model.addAttribute("webuserForm", webUserForm); return "data-man/webuserPassword"; } private static WebUserForm fromBase(WebUserBaseForm webUserBaseForm) { - WebUserForm webUserForm = new WebUserForm(); + var webUserForm = new WebUserForm(); webUserForm.setWebUserPk(webUserBaseForm.getWebUserPk()); webUserForm.setWebUsername(webUserBaseForm.getWebUsername()); webUserForm.setAuthorities(webUserBaseForm.getAuthorities()); @@ -144,14 +144,14 @@ public String passwordChange(@Valid @ModelAttribute("webuserForm") WebUserForm w return "data-man/webuserPassword"; } - webUserService.updatePassword(webuserForm); + webUsersService.updatePassword(webuserForm); return toDetails(webuserForm.getWebUserPk()); } @GetMapping(value = API_PASSWORD_PATH) public String apiPasswordChangeGet(@PathVariable("webUserName") String webUserName, Model model) { - WebUserBaseForm base = webUserService.getDetails(webUserName); - WebUserForm webUserForm = fromBase(base); + var base = webUsersService.getDetails(webUserName); + var webUserForm = fromBase(base); model.addAttribute("webuserForm", webUserForm); return "data-man/webuserApiPassword"; } @@ -163,13 +163,13 @@ public String apiPasswordChange(@Valid @ModelAttribute("webuserForm") WebUserFor return "data-man/webuserApiPassword"; } - webUserService.updateApiPassword(webuserForm); + webUsersService.updateApiPassword(webuserForm); return toDetails(webuserForm.getWebUserPk()); } @PostMapping(value = DELETE_PATH) public String delete(@PathVariable("webUserPk") Integer webUserPk) { - webUserService.deleteUser(webUserPk); + webUsersService.deleteUser(webUserPk); return toOverview(); } diff --git a/src/main/java/de/rwth/idsg/steve/web/dto/ReservationForm.java b/src/main/java/de/rwth/idsg/steve/web/dto/ReservationForm.java new file mode 100644 index 000000000..c16ecd28f --- /dev/null +++ b/src/main/java/de/rwth/idsg/steve/web/dto/ReservationForm.java @@ -0,0 +1,49 @@ +/* + * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve + * Copyright (C) 2013-2025 SteVe Community Team + * All Rights Reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.rwth.idsg.steve.web.dto; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import jakarta.validation.constraints.Future; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import org.joda.time.DateTime; + +@Getter +@Setter +@ToString +public class ReservationForm { + + @NotEmpty(message = "ID Tag is required") + private String idTag; + + @NotEmpty(message = "ChargeBox ID is required") + private String chargeBoxId; + + @NotNull(message = "Connector ID is required") + private Integer connectorId; + + @NotNull(message = "Start timestamp is required") + private DateTime startTimestamp; + + @NotNull(message = "Expiry timestamp is required") + @Future(message = "Expiry timestamp must be in the future") + private DateTime expiryTimestamp; +} diff --git a/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java b/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java index 5eafc5e90..b31d96450 100644 --- a/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java +++ b/src/test/java/de/rwth/idsg/steve/utils/__DatabasePreparer__.java @@ -143,7 +143,7 @@ public static ChargePoint.Details getCBDetails(String chargeboxID) { ChargePointRepositoryImpl impl = new ChargePointRepositoryImpl(dslContext, new AddressRepositoryImpl()); Map pkMap = impl.getChargeBoxIdPkPair(Arrays.asList(chargeboxID)); int pk = pkMap.get(chargeboxID); - return impl.getDetails(pk); + return impl.getDetails(pk).orElseThrow(); } private static void runOperation(Consumer consumer) { diff --git a/src/test/java/de/rwth/idsg/steve/web/api/AbstractControllerTest.java b/src/test/java/de/rwth/idsg/steve/web/api/AbstractControllerTest.java index 81fe9563d..562098b7d 100644 --- a/src/test/java/de/rwth/idsg/steve/web/api/AbstractControllerTest.java +++ b/src/test/java/de/rwth/idsg/steve/web/api/AbstractControllerTest.java @@ -21,20 +21,38 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import org.jetbrains.annotations.NotNull; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder; -public class AbstractControllerTest { +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; - ObjectMapper objectMapper; +public abstract class AbstractControllerTest { + + private static final String CONTENT_TYPE = "application/json"; + + protected final ObjectMapper objectMapper; AbstractControllerTest() { + this.objectMapper = createMapper(); + } + + private static @NotNull ObjectMapper createMapper() { ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build(); objectMapper.findAndRegisterModules(); // if the client sends unknown props, just ignore them instead of failing objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // default is true objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + return objectMapper; + } - this.objectMapper = objectMapper; + protected MockMvc buildMockMvc(StandaloneMockMvcBuilder builder) { + return builder.setControllerAdvice(new ApiControllerAdvice()) + .setMessageConverters(new MappingJackson2HttpMessageConverter(objectMapper)) + .alwaysExpect(content().contentType(CONTENT_TYPE)) + .build(); } } diff --git a/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java b/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java new file mode 100644 index 000000000..a1790f6cc --- /dev/null +++ b/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java @@ -0,0 +1,166 @@ +/* + * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve + * Copyright (C) 2013-2025 SteVe Community Team + * All Rights Reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.rwth.idsg.steve.web.api; + +import de.rwth.idsg.steve.repository.dto.ChargePoint; +import de.rwth.idsg.steve.service.ChargePointsService; +import de.rwth.idsg.steve.web.api.exception.NotFoundException; +import de.rwth.idsg.steve.web.dto.ChargePointForm; +import jooq.steve.db.tables.records.ChargeBoxRecord; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import java.util.Collections; +import java.util.List; + +import static org.hamcrest.Matchers.hasSize; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ExtendWith(MockitoExtension.class) +public class ChargePointsRestControllerTest extends AbstractControllerTest { + + @Mock + private ChargePointsService chargePointsService; + + private MockMvc mockMvc; + + @BeforeEach + public void setup() { + mockMvc = buildMockMvc(MockMvcBuilders.standaloneSetup(new ChargePointsRestController(chargePointsService))); + } + + @Test + @DisplayName("GET all: Test with empty results, expected 200") + public void testGet_withEmptyList() throws Exception { + when(chargePointsService.getOverview(any())).thenReturn(Collections.emptyList()); + + mockMvc.perform(get("/api/v1/chargeboxes")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(0))); + } + + @Test + @DisplayName("GET all: Test with one result, expected 200") + public void testGet_withOneResult() throws Exception { + List results = List.of(ChargePoint.Overview.builder().chargeBoxPk(1).build()); + when(chargePointsService.getOverview(any())).thenReturn(results); + + mockMvc.perform(get("/api/v1/chargeboxes")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(1))) + .andExpect(jsonPath("$[0].chargeBoxPk").value("1")); + } + + @Test + @DisplayName("GET one: Entity not found, expected 404") + public void testGetOne_notFound() throws Exception { + when(chargePointsService.getDetails(any(Integer.class))).thenThrow(new NotFoundException("")); + + mockMvc.perform(get("/api/v1/chargeboxes/1")) + .andExpect(status().isNotFound()); + } + + @Test + @DisplayName("GET one: One entity found, expected 200") + public void testGetOne_found() throws Exception { + ChargePoint.Details result = createDetails(1, null); + when(chargePointsService.getDetails(1)).thenReturn(result); + + mockMvc.perform(get("/api/v1/chargeboxes/1")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.chargeBoxPk").value("1")); + } + + private static ChargePoint.Details createDetails(Integer pk, String chargeBoxId) { + ChargeBoxRecord cbr = new ChargeBoxRecord(); + cbr.setChargeBoxPk(pk); + cbr.setChargeBoxId(chargeBoxId); + return new ChargePoint.Details(cbr, null); + } + + @Test + @DisplayName("POST: Entity created, expected 201") + public void testPost() throws Exception { + ChargePointForm form = new ChargePointForm(); + form.setChargeBoxId("test-cb"); + form.setRegistrationStatus("updated"); + form.setInsertConnectorStatusAfterTransactionMsg(true); + + ChargePoint.Details result = createDetails(1, "test-cb"); + + when(chargePointsService.addChargePoint(any(ChargePointForm.class))).thenReturn(1); + when(chargePointsService.getDetails(1)).thenReturn(result); + + mockMvc.perform(post("/api/v1/chargeboxes") + .content(objectMapper.writeValueAsString(form)) + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.chargeBoxPk").value("1")) + .andExpect(jsonPath("$.chargeBoxId").value("test-cb")); + } + + @Test + @DisplayName("PUT: Entity updated, expected 200") + public void testPut() throws Exception { + ChargePointForm form = new ChargePointForm(); + form.setChargeBoxId("test-cb-updated"); + form.setRegistrationStatus("updated"); + form.setInsertConnectorStatusAfterTransactionMsg(true); + + ChargePoint.Details result = createDetails(1, "test-cb-updated"); + + when(chargePointsService.getDetails(1)).thenReturn(result); + + mockMvc.perform(put("/api/v1/chargeboxes/1") + .content(objectMapper.writeValueAsString(form)) + .contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(status().isOk()); + + verify(chargePointsService).updateChargePoint(any(ChargePointForm.class)); + } + + @Test + @DisplayName("DELETE: Entity deleted, expected 200") + public void testDelete() throws Exception { + var rec = new ChargeBoxRecord(); + rec.setChargeBoxPk(1); + var result = new ChargePoint.Details(rec, null); + when(chargePointsService.getDetails(1)).thenReturn(result); + + mockMvc.perform(delete("/api/v1/chargeboxes/1")) + .andExpect(status().isOk()); + + verify(chargePointsService).deleteChargePoint(1); + } +} diff --git a/src/test/java/de/rwth/idsg/steve/web/api/OcppTagsRestControllerTest.java b/src/test/java/de/rwth/idsg/steve/web/api/OcppTagsRestControllerTest.java index c5d9d3042..0ec8524af 100644 --- a/src/test/java/de/rwth/idsg/steve/web/api/OcppTagsRestControllerTest.java +++ b/src/test/java/de/rwth/idsg/steve/web/api/OcppTagsRestControllerTest.java @@ -20,7 +20,7 @@ import de.rwth.idsg.steve.SteveException; import de.rwth.idsg.steve.repository.dto.OcppTag; -import de.rwth.idsg.steve.service.OcppTagService; +import de.rwth.idsg.steve.service.OcppTagsService; import de.rwth.idsg.steve.utils.DateTimeUtils; import de.rwth.idsg.steve.web.dto.OcppTagForm; import de.rwth.idsg.steve.web.dto.OcppTagQueryForm; @@ -33,7 +33,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultMatcher; import org.springframework.test.web.servlet.setup.MockMvcBuilders; @@ -56,7 +56,6 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -67,20 +66,14 @@ @ExtendWith(MockitoExtension.class) public class OcppTagsRestControllerTest extends AbstractControllerTest { - private static final String CONTENT_TYPE = "application/json"; - @Mock - private OcppTagService ocppTagService; + private OcppTagsService ocppTagsService; private MockMvc mockMvc; @BeforeEach public void setup() { - mockMvc = MockMvcBuilders.standaloneSetup(new OcppTagsRestController(ocppTagService)) - .setControllerAdvice(new ApiControllerAdvice()) - .setMessageConverters(new MappingJackson2HttpMessageConverter(objectMapper)) - .alwaysExpect(content().contentType(CONTENT_TYPE)) - .build(); + mockMvc = buildMockMvc(MockMvcBuilders.standaloneSetup(new OcppTagsRestController(ocppTagsService))); } @Test @@ -90,7 +83,7 @@ public void test1() throws Exception { List results = Collections.emptyList(); // when - when(ocppTagService.getOverview(any())).thenReturn(results); + when(ocppTagsService.getOverview(any())).thenReturn(results); // then mockMvc.perform(get("/api/v1/ocppTags")) @@ -105,7 +98,7 @@ public void test2() throws Exception { List results = List.of(OcppTag.OcppTagOverview.builder().ocppTagPk(96).build()); // when - when(ocppTagService.getOverview(any())).thenReturn(results); + when(ocppTagsService.getOverview(any())).thenReturn(results); // then mockMvc.perform(get("/api/v1/ocppTags")) @@ -118,7 +111,7 @@ public void test2() throws Exception { @DisplayName("GET all: Downstream bean throws exception, expected 500") public void test3() throws Exception { // when - when(ocppTagService.getOverview(any())).thenThrow(new RuntimeException("failed")); + when(ocppTagsService.getOverview(any())).thenThrow(new RuntimeException("failed")); // then mockMvc.perform(get("/api/v1/ocppTags")) @@ -156,7 +149,7 @@ public void test5() throws Exception { .build(); // when - when(ocppTagService.getOverview(any())).thenReturn(List.of(result)); + when(ocppTagsService.getOverview(any())).thenReturn(List.of(result)); // then mockMvc.perform(get("/api/v1/ocppTags") @@ -194,7 +187,7 @@ public void test6() throws Exception { @DisplayName("GET one: Entity not found, expected 404") public void test7() throws Exception { // when - when(ocppTagService.getOverview(any())).thenReturn(Collections.emptyList()); + when(ocppTagsService.getOverview(any())).thenReturn(Collections.emptyList()); // then mockMvc.perform(get("/api/v1/ocppTags/12")) @@ -209,7 +202,7 @@ public void test8() throws Exception { OcppTag.OcppTagOverview result = OcppTag.OcppTagOverview.builder().ocppTagPk(12).build(); // when - when(ocppTagService.getOverview(any())).thenReturn(List.of(result)); + when(ocppTagsService.getOverview(any())).thenReturn(List.of(result)); // then mockMvc.perform(get("/api/v1/ocppTags/12")) @@ -228,12 +221,12 @@ public void test9() throws Exception { mockMvc.perform( post("/api/v1/ocppTags") .content(objectMapper.writeValueAsString(form)) - .contentType(CONTENT_TYPE) + .contentType(MediaType.APPLICATION_JSON_VALUE) ) .andExpect(status().isBadRequest()) .andExpectAll(errorJsonMatchers()); - verifyNoInteractions(ocppTagService); + verifyNoInteractions(ocppTagsService); } @Test @@ -248,12 +241,12 @@ public void test10() throws Exception { mockMvc.perform( post("/api/v1/ocppTags") .content(objectMapper.writeValueAsString(form)) - .contentType(CONTENT_TYPE) + .contentType(MediaType.APPLICATION_JSON_VALUE) ) .andExpect(status().isBadRequest()) .andExpectAll(errorJsonMatchers()); - verifyNoInteractions(ocppTagService); + verifyNoInteractions(ocppTagsService); } @Test @@ -271,14 +264,14 @@ public void test11() throws Exception { .build(); // when - when(ocppTagService.addOcppTag(eq(form))).thenReturn(ocppTagPk); - when(ocppTagService.getOverview(any())).thenReturn(List.of(result)); + when(ocppTagsService.addOcppTag(eq(form))).thenReturn(ocppTagPk); + when(ocppTagsService.getOverview(any())).thenReturn(List.of(result)); // then mockMvc.perform( post("/api/v1/ocppTags") .content(objectMapper.writeValueAsString(form)) - .contentType(CONTENT_TYPE) + .contentType(MediaType.APPLICATION_JSON_VALUE) ) .andExpect(status().isCreated()) .andExpect(jsonPath("$.ocppTagPk").value("123")) @@ -303,17 +296,17 @@ public void test12() throws Exception { .build(); // when - when(ocppTagService.getOverview(any())).thenReturn(List.of(result)); + when(ocppTagsService.getOverview(any())).thenReturn(List.of(result)); // then mockMvc.perform( put("/api/v1/ocppTags/" + ocppTagPk) .content(objectMapper.writeValueAsString(form)) - .contentType(CONTENT_TYPE) + .contentType(MediaType.APPLICATION_JSON_VALUE) ) .andExpect(status().isOk()); - verify(ocppTagService).updateOcppTag(eq(form)); + verify(ocppTagsService).updateOcppTag(eq(form)); } @Test @@ -330,11 +323,11 @@ public void test13() throws Exception { mockMvc.perform( put("/api/v1/ocppTags/" + ocppTagPk) .content(objectMapper.writeValueAsString(form)) - .contentType(CONTENT_TYPE) + .contentType(MediaType.APPLICATION_JSON_VALUE) ) .andExpect(status().isBadRequest()); - verifyNoInteractions(ocppTagService); + verifyNoInteractions(ocppTagsService); } @Test @@ -349,13 +342,13 @@ public void test14() throws Exception { form.setNote("note-1"); // when - doThrow(new SteveException("failed")).when(ocppTagService).updateOcppTag(any()); + doThrow(new SteveException("failed")).when(ocppTagsService).updateOcppTag(any()); // then mockMvc.perform( put("/api/v1/ocppTags/" + ocppTagPk) .content(objectMapper.writeValueAsString(form)) - .contentType(CONTENT_TYPE) + .contentType(MediaType.APPLICATION_JSON_VALUE) ) .andExpect(status().isInternalServerError()) .andExpectAll(errorJsonMatchers()); @@ -374,14 +367,14 @@ public void test15() throws Exception { .build(); // when - when(ocppTagService.getOverview(any())).thenReturn(List.of(result)); + when(ocppTagsService.getOverview(any())).thenReturn(List.of(result)); // then mockMvc.perform(delete("/api/v1/ocppTags/" + ocppTagPk)) .andExpect(status().isOk()) .andExpect(jsonPath("$.ocppTagPk").value("123")); - verify(ocppTagService).deleteOcppTag(eq(ocppTagPk)); + verify(ocppTagsService).deleteOcppTag(ocppTagPk); } @Test @@ -397,8 +390,8 @@ public void test16() throws Exception { .build(); // when - when(ocppTagService.getOverview(any())).thenReturn(List.of(result)); - doThrow(new SteveException("failed")).when(ocppTagService).deleteOcppTag(eq(ocppTagPk)); + when(ocppTagsService.getOverview(any())).thenReturn(List.of(result)); + doThrow(new SteveException("failed")).when(ocppTagsService).deleteOcppTag(ocppTagPk); // then mockMvc.perform(delete("/api/v1/ocppTags/" + ocppTagPk)) @@ -413,14 +406,14 @@ public void test17() throws Exception { int ocppTagPk = 123; // when - when(ocppTagService.getOverview(any())).thenReturn(List.of()); + when(ocppTagsService.getOverview(any())).thenReturn(List.of()); // then mockMvc.perform(delete("/api/v1/ocppTags/" + ocppTagPk)) .andExpect(status().isNotFound()) .andExpectAll(errorJsonMatchers()); - verify(ocppTagService, times(0)).deleteOcppTag(anyInt()); + verify(ocppTagsService, times(0)).deleteOcppTag(anyInt()); } @Test @@ -433,19 +426,19 @@ public void test18() throws Exception { form.setIdTag("id-123"); // when - when(ocppTagService.addOcppTag(eq(form))).thenThrow(new SteveException.AlreadyExists("A user with idTag '%s' already exists.", ocppTagPk)); + when(ocppTagsService.addOcppTag(eq(form))).thenThrow(new SteveException.AlreadyExists("A user with idTag '%s' already exists.", ocppTagPk)); // then mockMvc.perform( post("/api/v1/ocppTags") .content(objectMapper.writeValueAsString(form)) - .contentType(CONTENT_TYPE) + .contentType(MediaType.APPLICATION_JSON_VALUE) ) .andExpect(status().isUnprocessableEntity()) .andExpectAll(errorJsonMatchers()); - verify(ocppTagService, times(0)).removeUnknown(anyList()); - verify(ocppTagService, times(0)).getOverview(any(OcppTagQueryForm.OcppTagQueryFormForApi.class)); + verify(ocppTagsService, times(0)).removeUnknown(anyList()); + verify(ocppTagsService, times(0)).getOverview(any(OcppTagQueryForm.OcppTagQueryFormForApi.class)); } @Test @@ -455,19 +448,19 @@ public void test19() throws Exception { ArgumentCaptor formToCapture = ArgumentCaptor.forClass(OcppTagQueryForm.OcppTagQueryFormForApi.class); // when - when(ocppTagService.getOverview(any())).thenReturn(Collections.emptyList()); + when(ocppTagsService.getOverview(any())).thenReturn(Collections.emptyList()); // then mockMvc.perform(get("/api/v1/ocppTags") .param("expired", "FALSE")) .andExpect(status().isOk()); - verify(ocppTagService).getOverview(formToCapture.capture()); + verify(ocppTagsService).getOverview(formToCapture.capture()); OcppTagQueryForm.OcppTagQueryFormForApi capturedForm = formToCapture.getValue(); - assertEquals(capturedForm.getExpired(), OcppTagQueryForm.BooleanType.FALSE); - assertEquals(capturedForm.getInTransaction(), OcppTagQueryForm.BooleanType.ALL); - assertEquals(capturedForm.getBlocked(), OcppTagQueryForm.BooleanType.ALL); + assertEquals(OcppTagQueryForm.BooleanType.FALSE, capturedForm.getExpired()); + assertEquals(OcppTagQueryForm.BooleanType.ALL, capturedForm.getInTransaction()); + assertEquals(OcppTagQueryForm.BooleanType.ALL, capturedForm.getBlocked()); } @Test @@ -477,19 +470,19 @@ public void test20() throws Exception { ArgumentCaptor formToCapture = ArgumentCaptor.forClass(OcppTagQueryForm.OcppTagQueryFormForApi.class); // when - when(ocppTagService.getOverview(any())).thenReturn(Collections.emptyList()); + when(ocppTagsService.getOverview(any())).thenReturn(Collections.emptyList()); // then mockMvc.perform(get("/api/v1/ocppTags") .param("inTransaction", "TRUE")) .andExpect(status().isOk()); - verify(ocppTagService).getOverview(formToCapture.capture()); + verify(ocppTagsService).getOverview(formToCapture.capture()); OcppTagQueryForm.OcppTagQueryFormForApi capturedForm = formToCapture.getValue(); - assertEquals(capturedForm.getExpired(), OcppTagQueryForm.BooleanType.ALL); - assertEquals(capturedForm.getInTransaction(), OcppTagQueryForm.BooleanType.TRUE); - assertEquals(capturedForm.getBlocked(), OcppTagQueryForm.BooleanType.ALL); + assertEquals(OcppTagQueryForm.BooleanType.ALL, capturedForm.getExpired()); + assertEquals(OcppTagQueryForm.BooleanType.TRUE, capturedForm.getInTransaction()); + assertEquals(OcppTagQueryForm.BooleanType.ALL, capturedForm.getBlocked()); } @Test @@ -499,19 +492,19 @@ public void test21() throws Exception { ArgumentCaptor formToCapture = ArgumentCaptor.forClass(OcppTagQueryForm.OcppTagQueryFormForApi.class); // when - when(ocppTagService.getOverview(any())).thenReturn(Collections.emptyList()); + when(ocppTagsService.getOverview(any())).thenReturn(Collections.emptyList()); // then mockMvc.perform(get("/api/v1/ocppTags") .param("blocked", "FALSE")) .andExpect(status().isOk()); - verify(ocppTagService).getOverview(formToCapture.capture()); + verify(ocppTagsService).getOverview(formToCapture.capture()); OcppTagQueryForm.OcppTagQueryFormForApi capturedForm = formToCapture.getValue(); - assertEquals(capturedForm.getExpired(), OcppTagQueryForm.BooleanType.ALL); - assertEquals(capturedForm.getInTransaction(), OcppTagQueryForm.BooleanType.ALL); - assertEquals(capturedForm.getBlocked(), OcppTagQueryForm.BooleanType.FALSE); + assertEquals(OcppTagQueryForm.BooleanType.ALL, capturedForm.getExpired()); + assertEquals(OcppTagQueryForm.BooleanType.ALL, capturedForm.getInTransaction()); + assertEquals(OcppTagQueryForm.BooleanType.FALSE, capturedForm.getBlocked()); } private static ResultMatcher[] errorJsonMatchers() { diff --git a/src/test/java/de/rwth/idsg/steve/web/api/ReservationRestControllerTest.java b/src/test/java/de/rwth/idsg/steve/web/api/ReservationRestControllerTest.java new file mode 100644 index 000000000..a8458da57 --- /dev/null +++ b/src/test/java/de/rwth/idsg/steve/web/api/ReservationRestControllerTest.java @@ -0,0 +1,51 @@ +/* + * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve + * Copyright (C) 2013-2025 SteVe Community Team + * All Rights Reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.rwth.idsg.steve.web.api; + +import de.rwth.idsg.steve.service.ReservationsService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ExtendWith(MockitoExtension.class) +class ReservationRestControllerTest extends AbstractControllerTest { + + @Mock + private ReservationsService reservationsService; + + private MockMvc mockMvc; + + @BeforeEach + public void setup() { + mockMvc = buildMockMvc(MockMvcBuilders.standaloneSetup(new ReservationsRestController(reservationsService))); + } + + @Test + void testGetReservations() throws Exception { + mockMvc.perform(get("/api/reservations")) + .andExpect(status().isOk()); + } +} diff --git a/src/test/java/de/rwth/idsg/steve/web/api/TransactionRestControllerTest.java b/src/test/java/de/rwth/idsg/steve/web/api/TransactionRestControllerTest.java index 386fbf0e4..9d5ec9f24 100644 --- a/src/test/java/de/rwth/idsg/steve/web/api/TransactionRestControllerTest.java +++ b/src/test/java/de/rwth/idsg/steve/web/api/TransactionRestControllerTest.java @@ -29,7 +29,6 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultMatcher; import org.springframework.test.web.servlet.setup.MockMvcBuilders; @@ -43,7 +42,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -61,11 +59,7 @@ public class TransactionRestControllerTest extends AbstractControllerTest { @BeforeEach public void setup() { - mockMvc = MockMvcBuilders.standaloneSetup(new TransactionsRestController(transactionRepository)) - .setControllerAdvice(new ApiControllerAdvice()) - .setMessageConverters(new MappingJackson2HttpMessageConverter(objectMapper)) - .alwaysExpect(content().contentType("application/json")) - .build(); + mockMvc = buildMockMvc(MockMvcBuilders.standaloneSetup(new TransactionsRestController(transactionRepository))); } @Test @@ -226,8 +220,8 @@ public void test10() throws Exception { verify(transactionRepository).getTransactions(formToCapture.capture()); TransactionQueryForm.TransactionQueryFormForApi capturedForm = formToCapture.getValue(); - assertEquals(capturedForm.getType(), TransactionQueryForm.QueryType.ACTIVE); - assertEquals(capturedForm.getPeriodType(), TransactionQueryForm.QueryPeriodType.ALL); + assertEquals(TransactionQueryForm.QueryType.ACTIVE, capturedForm.getType()); + assertEquals(TransactionQueryForm.QueryPeriodType.ALL, capturedForm.getPeriodType()); } @Test @@ -247,8 +241,8 @@ public void test11() throws Exception { verify(transactionRepository).getTransactions(formToCapture.capture()); TransactionQueryForm.TransactionQueryFormForApi capturedForm = formToCapture.getValue(); - assertEquals(capturedForm.getType(), TransactionQueryForm.QueryType.ALL); - assertEquals(capturedForm.getPeriodType(), TransactionQueryForm.QueryPeriodType.LAST_30); + assertEquals(TransactionQueryForm.QueryType.ALL, capturedForm.getType()); + assertEquals(TransactionQueryForm.QueryPeriodType.LAST_30, capturedForm.getPeriodType()); } private static ResultMatcher[] errorJsonMatchers() { diff --git a/src/test/java/de/rwth/idsg/steve/web/api/UsersRestControllerTest.java b/src/test/java/de/rwth/idsg/steve/web/api/UsersRestControllerTest.java new file mode 100644 index 000000000..724dcc219 --- /dev/null +++ b/src/test/java/de/rwth/idsg/steve/web/api/UsersRestControllerTest.java @@ -0,0 +1,51 @@ +/* + * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve + * Copyright (C) 2013-2025 SteVe Community Team + * All Rights Reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.rwth.idsg.steve.web.api; + +import de.rwth.idsg.steve.service.UsersService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ExtendWith(MockitoExtension.class) +class UsersRestControllerTest extends AbstractControllerTest { + + @Mock + private UsersService usersService; + + private MockMvc mockMvc; + + @BeforeEach + public void setup() { + mockMvc = buildMockMvc(MockMvcBuilders.standaloneSetup(new UsersRestController(usersService))); + } + + @Test + void testGetUsers() throws Exception { + mockMvc.perform(get("/api/users")) + .andExpect(status().isOk()); + } +} From 45cd22dbedc145b7fd6219b5febc39c3def0bdb9 Mon Sep 17 00:00:00 2001 From: Julien Herr Date: Tue, 19 Aug 2025 19:41:08 +0200 Subject: [PATCH 2/4] fix: update after CodeRabbit review --- .../steve/repository/OcppTagRepository.java | 2 +- .../impl/OcppTagRepositoryImpl.java | 2 +- .../impl/ReservationRepositoryImpl.java | 38 +++++++++---------- .../repository/impl/UserRepositoryImpl.java | 29 +++++++------- .../CentralSystemService16_Service.java | 12 +++--- .../idsg/steve/service/OcppTagsService.java | 2 +- .../rwth/idsg/steve/service/UsersService.java | 8 ++-- .../idsg/steve/service/WebUsersService.java | 23 ++++++----- .../web/api/ChargePointsRestController.java | 15 +++++--- .../steve/web/api/OcppTagsRestController.java | 20 +++++++--- .../web/api/RemoteCommandsRestController.java | 2 +- .../web/api/ReservationsRestController.java | 17 +++++++-- .../steve/web/api/TasksRestController.java | 2 +- .../steve/web/api/UsersRestController.java | 23 ++++++++--- .../controller/AboutSettingsController.java | 5 ++- .../web/controller/AjaxCallController.java | 7 +++- .../controller/ChargePointsController.java | 7 +++- .../ChargingProfilesController.java | 6 ++- .../steve/web/controller/LogController.java | 8 +++- .../web/controller/Ocpp12Controller.java | 5 ++- .../web/controller/Ocpp15Controller.java | 5 ++- .../web/controller/Ocpp16Controller.java | 5 ++- .../controller/ReservationsController.java | 2 +- .../steve/web/controller/TasksController.java | 33 ++++++++-------- .../idsg/steve/web/dto/ReservationForm.java | 9 +++-- .../steve/web/api/AbstractControllerTest.java | 5 +-- .../api/ChargePointsRestControllerTest.java | 3 +- .../web/api/OcppTagsRestControllerTest.java | 2 +- .../api/ReservationRestControllerTest.java | 14 ++++++- .../api/TransactionRestControllerTest.java | 1 - .../web/api/UsersRestControllerTest.java | 10 ++++- 31 files changed, 207 insertions(+), 115 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/repository/OcppTagRepository.java b/src/main/java/de/rwth/idsg/steve/repository/OcppTagRepository.java index e3dc94f79..b8481a00d 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/OcppTagRepository.java +++ b/src/main/java/de/rwth/idsg/steve/repository/OcppTagRepository.java @@ -44,7 +44,7 @@ public interface OcppTagRepository { List getActiveIdTags(); List getParentIdTags(); - String getParentIdtag(String idTag); + String getParentIdTag(String idTag); void addOcppTagList(List idTagList); int addOcppTag(OcppTagForm form); diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/OcppTagRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/OcppTagRepositoryImpl.java index e02b1a07f..fbb1e9477 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/OcppTagRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/OcppTagRepositoryImpl.java @@ -194,7 +194,7 @@ public List getParentIdTags() { } @Override - public String getParentIdtag(String idTag) { + public String getParentIdTag(String idTag) { return ctx.select(OCPP_TAG.PARENT_ID_TAG) .from(OCPP_TAG) .where(OCPP_TAG.ID_TAG.eq(idTag)) diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/ReservationRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/ReservationRepositoryImpl.java index 7b784d884..2d2d49a9b 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/ReservationRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/ReservationRepositoryImpl.java @@ -60,26 +60,24 @@ public class ReservationRepositoryImpl implements ReservationRepository { @Override public Optional getReservation(int id) { - SelectQuery selectQuery = ctx.selectQuery(); - selectQuery.addFrom(RESERVATION); - selectQuery.addJoin(OCPP_TAG, OCPP_TAG.ID_TAG.eq(RESERVATION.ID_TAG)); - selectQuery.addJoin(CONNECTOR, CONNECTOR.CONNECTOR_PK.eq(RESERVATION.CONNECTOR_PK)); - selectQuery.addJoin(CHARGE_BOX, CONNECTOR.CHARGE_BOX_ID.eq(CHARGE_BOX.CHARGE_BOX_ID)); - - selectQuery.addSelect( - RESERVATION.RESERVATION_PK, - RESERVATION.TRANSACTION_PK, - OCPP_TAG.OCPP_TAG_PK, - CHARGE_BOX.CHARGE_BOX_PK, - OCPP_TAG.ID_TAG, - CHARGE_BOX.CHARGE_BOX_ID, - RESERVATION.START_DATETIME, - RESERVATION.EXPIRY_DATETIME, - RESERVATION.STATUS, - CONNECTOR.CONNECTOR_ID - ); - selectQuery.addConditions(RESERVATION.RESERVATION_PK.eq(id)); - Reservation result = (Reservation) selectQuery.fetchOne(new ReservationMapper()); + Reservation result = ctx.select( + RESERVATION.RESERVATION_PK, + RESERVATION.TRANSACTION_PK, + OCPP_TAG.OCPP_TAG_PK, + CHARGE_BOX.CHARGE_BOX_PK, + OCPP_TAG.ID_TAG, + CHARGE_BOX.CHARGE_BOX_ID, + RESERVATION.START_DATETIME, + RESERVATION.EXPIRY_DATETIME, + RESERVATION.STATUS, + CONNECTOR.CONNECTOR_ID + ) + .from(RESERVATION) + .join(OCPP_TAG).on(OCPP_TAG.ID_TAG.eq(RESERVATION.ID_TAG)) + .join(CONNECTOR).on(CONNECTOR.CONNECTOR_PK.eq(RESERVATION.CONNECTOR_PK)) + .join(CHARGE_BOX).on(CONNECTOR.CHARGE_BOX_ID.eq(CHARGE_BOX.CHARGE_BOX_ID)) + .where(RESERVATION.RESERVATION_PK.eq(id)) + .fetchOne(new ReservationMapper()); return Optional.ofNullable(result); } diff --git a/src/main/java/de/rwth/idsg/steve/repository/impl/UserRepositoryImpl.java b/src/main/java/de/rwth/idsg/steve/repository/impl/UserRepositoryImpl.java index a332cdb2b..afe06dee6 100644 --- a/src/main/java/de/rwth/idsg/steve/repository/impl/UserRepositoryImpl.java +++ b/src/main/java/de/rwth/idsg/steve/repository/impl/UserRepositoryImpl.java @@ -25,7 +25,6 @@ import de.rwth.idsg.steve.repository.dto.User; import de.rwth.idsg.steve.web.dto.UserForm; import de.rwth.idsg.steve.web.dto.UserQueryForm; -import jooq.steve.db.tables.records.UserRecord; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.jooq.Condition; @@ -40,7 +39,11 @@ import org.springframework.stereotype.Repository; import org.springframework.util.CollectionUtils; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; import static de.rwth.idsg.steve.utils.CustomDSL.includes; import static jooq.steve.db.Tables.USER_OCPP_TAG; @@ -94,11 +97,11 @@ public Optional getDetails(int userPk) { @Override public Integer add(UserForm form) { return ctx.transactionResult(configuration -> { - var ctx = DSL.using(configuration); + var tx = DSL.using(configuration); try { - var addressId = addressRepository.updateOrInsert(ctx, form.getAddress()); - var userPk = addInternal(ctx, form, addressId); - refreshOcppTagsInternal(ctx, form, userPk); + var addressId = addressRepository.updateOrInsert(tx, form.getAddress()); + var userPk = addInternal(tx, form, addressId); + refreshOcppTagsInternal(tx, form, userPk); return userPk; } catch (DataAccessException e) { throw new SteveException("Failed to add the user", e); @@ -109,11 +112,11 @@ public Integer add(UserForm form) { @Override public void update(UserForm form) { ctx.transaction(configuration -> { - var ctx = DSL.using(configuration); + var tx = DSL.using(configuration); try { - var addressId = addressRepository.updateOrInsert(ctx, form.getAddress()); - updateInternal(ctx, form, addressId); - refreshOcppTagsInternal(ctx, form, form.getUserPk()); + var addressId = addressRepository.updateOrInsert(tx, form.getAddress()); + updateInternal(tx, form, addressId); + refreshOcppTagsInternal(tx, form, form.getUserPk()); } catch (DataAccessException e) { throw new SteveException("Failed to update the user", e); @@ -124,10 +127,10 @@ public void update(UserForm form) { @Override public void delete(int userPk) { ctx.transaction(configuration -> { - var ctx = DSL.using(configuration); + var tx = DSL.using(configuration); try { - addressRepository.delete(ctx, selectAddressId(userPk)); - deleteInternal(ctx, userPk); + addressRepository.delete(tx, selectAddressId(userPk)); + deleteInternal(tx, userPk); } catch (DataAccessException e) { throw new SteveException("Failed to delete the user", e); } diff --git a/src/main/java/de/rwth/idsg/steve/service/CentralSystemService16_Service.java b/src/main/java/de/rwth/idsg/steve/service/CentralSystemService16_Service.java index 565d6cb1b..ec80d0ece 100644 --- a/src/main/java/de/rwth/idsg/steve/service/CentralSystemService16_Service.java +++ b/src/main/java/de/rwth/idsg/steve/service/CentralSystemService16_Service.java @@ -30,6 +30,7 @@ import de.rwth.idsg.steve.service.notification.OcppTransactionEnded; import de.rwth.idsg.steve.service.notification.OcppTransactionStarted; import jooq.steve.db.enums.TransactionStopEventActor; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import ocpp.cs._2015._10.AuthorizationStatus; import ocpp.cs._2015._10.AuthorizeRequest; @@ -69,14 +70,15 @@ */ @Slf4j @Service +@RequiredArgsConstructor public class CentralSystemService16_Service { - @Autowired private OcppServerRepository ocppServerRepository; - @Autowired private SettingsRepository settingsRepository; + private final OcppServerRepository ocppServerRepository; + private final SettingsRepository settingsRepository; - @Autowired private OcppTagsService ocppTagsService; - @Autowired private ApplicationEventPublisher applicationEventPublisher; - @Autowired private ChargePointHelperService chargePointHelperService; + private final OcppTagsService ocppTagsService; + private final ApplicationEventPublisher applicationEventPublisher; + private final ChargePointHelperService chargePointHelperService; public BootNotificationResponse bootNotification(BootNotificationRequest parameters, String chargeBoxIdentity, OcppProtocol ocppProtocol) { diff --git a/src/main/java/de/rwth/idsg/steve/service/OcppTagsService.java b/src/main/java/de/rwth/idsg/steve/service/OcppTagsService.java index 980ce7218..b99271093 100644 --- a/src/main/java/de/rwth/idsg/steve/service/OcppTagsService.java +++ b/src/main/java/de/rwth/idsg/steve/service/OcppTagsService.java @@ -80,7 +80,7 @@ public List getParentIdTags() { } public String getParentIdtag(String idTag) { - return ocppTagRepository.getParentIdtag(idTag); + return ocppTagRepository.getParentIdTag(idTag); } public List getAuthDataOfAllTags() { diff --git a/src/main/java/de/rwth/idsg/steve/service/UsersService.java b/src/main/java/de/rwth/idsg/steve/service/UsersService.java index 2fafa84eb..544dd027b 100644 --- a/src/main/java/de/rwth/idsg/steve/service/UsersService.java +++ b/src/main/java/de/rwth/idsg/steve/service/UsersService.java @@ -21,6 +21,7 @@ import de.rwth.idsg.steve.SteveException; import de.rwth.idsg.steve.repository.UserRepository; import de.rwth.idsg.steve.repository.dto.User; +import de.rwth.idsg.steve.web.api.exception.BadRequestException; import de.rwth.idsg.steve.web.api.exception.NotFoundException; import de.rwth.idsg.steve.web.dto.UserForm; import de.rwth.idsg.steve.web.dto.UserQueryForm; @@ -35,10 +36,6 @@ public class UsersService { private final UserRepository userRepository; - public List getUsers(UserQueryForm form) { - return userRepository.getOverview(form); - } - public User.Details getDetails(int userPk) { return userRepository.getDetails(userPk).orElseThrow( () -> new NotFoundException(String.format("User with id %d not found", userPk)) @@ -46,6 +43,9 @@ public User.Details getDetails(int userPk) { } public User.Details add(UserForm form) { + if (form.getUserPk() == null) { + throw new BadRequestException("userPk must not be null"); + } var id = userRepository.add(form); return userRepository.getDetails(id).orElseThrow( () -> new SteveException("User not found after creation, this should never happen") diff --git a/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java b/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java index 43cfd0057..77934e587 100644 --- a/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java +++ b/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java @@ -112,6 +112,7 @@ public void createUser(UserDetails user) { validateUserDetails(user); var record = toWebUserRecord(user); webUserRepository.createUser(record); + userCache.invalidate(user.getUsername()); } @Override @@ -119,11 +120,13 @@ public void updateUser(UserDetails user) { validateUserDetails(user); var record = toWebUserRecord(user); webUserRepository.updateUser(record); + userCache.invalidate(user.getUsername()); } @Override public void deleteUser(String username) { webUserRepository.deleteUser(username); + userCache.invalidate(username); } /** @@ -156,17 +159,17 @@ public boolean userExists(String username) { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - WebUserRecord record = webUserRepository.loadUserByUsername(username); + WebUserRecord webUser = webUserRepository.loadUserByUsername(username); - if (record == null) { + if (webUser == null) { throw new UsernameNotFoundException(username); } return User - .withUsername(record.getUsername()) - .password(record.getPassword()) - .disabled(!record.getEnabled()) - .authorities(fromJson(record.getAuthorities())) + .withUsername(webUser.getUsername()) + .password(webUser.getPassword()) + .disabled(!webUser.getEnabled()) + .authorities(fromJson(webUser.getAuthorities())) .build(); } @@ -190,6 +193,7 @@ public void deleteUser(int webUserPk) { public void changeStatusOfUser(String username, boolean enabled) { webUserRepository.changeStatusOfUser(username, enabled); + userCache.invalidate(username); } public boolean hasUserWithAuthority(String authority) { @@ -210,6 +214,7 @@ public void update(WebUserBaseForm form) { record.setEnabled(form.getEnabled()); record.setAuthorities(form.getAuthorities().getJsonValue()); webUserRepository.updateUserByPk(record); + userCache.invalidate(form.getWebUsername()); } public void updatePassword(WebUserForm form) { @@ -304,10 +309,8 @@ private UserDetails toUserDetailsBaseForm(WebUserBaseForm form) { } private UserDetails toUserDetails(WebUserForm form) { - String encPw = ""; - if (form.getPassword() != null) { - encPw = encoder.encode(form.getPassword()); - } + Assert.hasText(form.getPassword(), "Password may not be empty"); + String encPw = encoder.encode(form.getPassword()); return User .withUsername(form.getWebUsername()) .password(encPw) diff --git a/src/main/java/de/rwth/idsg/steve/web/api/ChargePointsRestController.java b/src/main/java/de/rwth/idsg/steve/web/api/ChargePointsRestController.java index ec245db00..6079ba4bf 100644 --- a/src/main/java/de/rwth/idsg/steve/web/api/ChargePointsRestController.java +++ b/src/main/java/de/rwth/idsg/steve/web/api/ChargePointsRestController.java @@ -28,8 +28,8 @@ import lombok.Data; import lombok.RequiredArgsConstructor; import org.springdoc.core.annotations.ParameterObject; -import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -37,8 +37,8 @@ import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import java.util.List; @@ -67,10 +67,15 @@ public ApiChargePoint getOne(@PathVariable("chargePointPk") Integer chargePointP @Operation(description = "Creates a new ChargePoint with the provided parameters.") @StandardApiResponses @PostMapping - @ResponseStatus(HttpStatus.CREATED) - public ApiChargePoint create(@RequestBody @Valid ChargePointForm params) { + public ResponseEntity create(@RequestBody @Valid ChargePointForm params) { var chargepointPk = chargePointsService.addChargePoint(params); - return toDto(chargePointsService.getDetails(chargepointPk)); + var body = toDto(chargePointsService.getDetails(chargepointPk)); + var location = ServletUriComponentsBuilder + .fromCurrentRequest() + .path("/{id}") + .buildAndExpand(body.getChargeBoxPk()) + .toUri(); + return ResponseEntity.created(location).body(body); } @Operation(description = "Updates an existing ChargePoint with the provided parameters.") diff --git a/src/main/java/de/rwth/idsg/steve/web/api/OcppTagsRestController.java b/src/main/java/de/rwth/idsg/steve/web/api/OcppTagsRestController.java index 4e80521f4..6d01dbc88 100644 --- a/src/main/java/de/rwth/idsg/steve/web/api/OcppTagsRestController.java +++ b/src/main/java/de/rwth/idsg/steve/web/api/OcppTagsRestController.java @@ -34,6 +34,7 @@ import org.springdoc.core.annotations.ParameterObject; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -45,8 +46,12 @@ import org.springframework.web.bind.annotation.RestController; import jakarta.validation.Valid; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + import java.util.List; +import static org.springframework.http.ResponseEntity.*; + /** * @author Sevket Goekay * @since 13.09.2022 @@ -98,11 +103,16 @@ public OcppTagOverview getOne(@PathVariable("ocppTagPk") Integer ocppTagPk) { @ApiResponse(responseCode = "500", description = "Internal Server Error", content = {@Content(mediaType = "application/json", schema = @Schema(implementation = ApiErrorResponse.class))})} ) @PostMapping - @ResponseStatus(HttpStatus.CREATED) - public OcppTagOverview create(@RequestBody @Valid OcppTagForm params) { - int ocppTagPk = ocppTagsService.addOcppTag(params); + public ResponseEntity create(@RequestBody @Valid OcppTagForm params) { + var ocppTagPk = ocppTagsService.addOcppTag(params); + var body = getOneInternal(ocppTagPk); - return getOneInternal(ocppTagPk); + var location = ServletUriComponentsBuilder + .fromCurrentRequest() + .path("/{ocppTagPk}") + .buildAndExpand(ocppTagPk) + .toUri(); + return created(location).body(body); } @Operation(description = """ @@ -136,7 +146,7 @@ private OcppTagOverview getOneInternal(int ocppTagPk) { var results = ocppTagsService.getOverview(params); if (results.isEmpty()) { - throw new NotFoundException("Could not find this ocppTag"); + throw new NotFoundException("Could not find ocppTag with id " + ocppTagPk); } return results.get(0); } diff --git a/src/main/java/de/rwth/idsg/steve/web/api/RemoteCommandsRestController.java b/src/main/java/de/rwth/idsg/steve/web/api/RemoteCommandsRestController.java index 25ea554fd..d2e3946c8 100644 --- a/src/main/java/de/rwth/idsg/steve/web/api/RemoteCommandsRestController.java +++ b/src/main/java/de/rwth/idsg/steve/web/api/RemoteCommandsRestController.java @@ -220,7 +220,7 @@ public Integer postRemoteStopTx(@Valid ApiChargePointStop params) { String.format("Transaction %s not found!", transactionId) ) ).getOcppIdTag(); - if (!params.getOcppTag().contentEquals(ocppTag)) { + if (!ocppTag.contentEquals(params.getOcppTag())) { throw new BadRequestException("The transaction was authorised with another OCPP Tag!"); } transactionParams.setTransactionId(transactionId); diff --git a/src/main/java/de/rwth/idsg/steve/web/api/ReservationsRestController.java b/src/main/java/de/rwth/idsg/steve/web/api/ReservationsRestController.java index 9e10e70a4..534b48f40 100644 --- a/src/main/java/de/rwth/idsg/steve/web/api/ReservationsRestController.java +++ b/src/main/java/de/rwth/idsg/steve/web/api/ReservationsRestController.java @@ -28,7 +28,9 @@ import lombok.RequiredArgsConstructor; import jakarta.validation.Valid; +import org.springdoc.core.annotations.ParameterObject; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -36,6 +38,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import java.util.List; @@ -48,16 +51,22 @@ public class ReservationsRestController { private final ReservationsService reservationsService; @GetMapping - public List getReservations(ReservationQueryForm form) { + public List getReservations(@ParameterObject ReservationQueryForm form) { return reservationsService.getReservations(form); } - @PostMapping - public Reservation addReservation(@Valid @RequestBody ReservationForm form) { + @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity addReservation(@Valid @RequestBody ReservationForm form) { var id = reservationsService.addReservation(form); - return reservationsService.getReservation(id).orElseThrow( + var body = reservationsService.getReservation(id).orElseThrow( () -> new SteveException("Reservation not found after creation, this should never happen") ); + var location = ServletUriComponentsBuilder + .fromCurrentRequest() + .path("/{id}") + .buildAndExpand(id) + .toUri(); + return ResponseEntity.created(location).body(body); } @DeleteMapping("/{id}") diff --git a/src/main/java/de/rwth/idsg/steve/web/api/TasksRestController.java b/src/main/java/de/rwth/idsg/steve/web/api/TasksRestController.java index 87e09a9d7..5e94cd4d8 100644 --- a/src/main/java/de/rwth/idsg/steve/web/api/TasksRestController.java +++ b/src/main/java/de/rwth/idsg/steve/web/api/TasksRestController.java @@ -41,7 +41,7 @@ * @author fnkbsi * @since 18.10.2023 */ -@Tag(name = "tasks") +@Tag(name = "tasks", description = "Task management operations") @Validated @RestController @RequestMapping(value = "/api/v1/tasks", produces = MediaType.APPLICATION_JSON_VALUE) diff --git a/src/main/java/de/rwth/idsg/steve/web/api/UsersRestController.java b/src/main/java/de/rwth/idsg/steve/web/api/UsersRestController.java index a0d216b26..a196486f5 100644 --- a/src/main/java/de/rwth/idsg/steve/web/api/UsersRestController.java +++ b/src/main/java/de/rwth/idsg/steve/web/api/UsersRestController.java @@ -26,7 +26,10 @@ import lombok.RequiredArgsConstructor; import jakarta.validation.Valid; +import org.springdoc.core.annotations.ParameterObject; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -34,7 +37,9 @@ import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import java.util.List; @@ -47,8 +52,8 @@ public class UsersRestController { private final UsersService usersService; @GetMapping - public List getUsers(UserQueryForm form) { - return usersService.getUsers(form); + public List getUsers(@ParameterObject UserQueryForm form) { + return usersService.getOverview(form); } @GetMapping("/{id}") @@ -56,12 +61,18 @@ public User.Details getUser(@PathVariable int id) { return usersService.getDetails(id); } - @PostMapping - public User.Details addUser(@Valid @RequestBody UserForm form) { - return usersService.add(form); + @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity addUser(@Valid @RequestBody UserForm form) { + var body = usersService.add(form); + var location = ServletUriComponentsBuilder + .fromCurrentRequest() + .path("/{id}") + .buildAndExpand(body.getUserRecord().getUserPk()) + .toUri(); + return ResponseEntity.created(location).body(body); } - @PutMapping("/{id}") + @PutMapping(path = "/{id}", consumes = MediaType.APPLICATION_JSON_VALUE) public User.Details updateUser(@PathVariable int id, @Valid @RequestBody UserForm form) { form.setUserPk(id); return usersService.update(form); diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java b/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java index de4cc889b..da0d8478b 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/AboutSettingsController.java @@ -31,9 +31,12 @@ import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.*; import jakarta.validation.Valid; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; import static de.rwth.idsg.steve.SteveConfiguration.CONFIG; diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/AjaxCallController.java b/src/main/java/de/rwth/idsg/steve/web/controller/AjaxCallController.java index 6aa21ae56..1101b23c1 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/AjaxCallController.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/AjaxCallController.java @@ -28,10 +28,14 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.*; import jakarta.annotation.PostConstruct; import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + import java.io.IOException; import java.util.List; @@ -112,6 +116,7 @@ private String serializeArray(List list) { */ private static void writeOutput(HttpServletResponse response, String str) throws IOException { response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.setCharacterEncoding(java.nio.charset.StandardCharsets.UTF_8.name()); response.getWriter().write(str); } } diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/ChargePointsController.java b/src/main/java/de/rwth/idsg/steve/web/controller/ChargePointsController.java index 020bbfe7e..53e66f299 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/ChargePointsController.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/ChargePointsController.java @@ -31,9 +31,14 @@ import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.*; import jakarta.validation.Valid; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + import java.util.Arrays; import java.util.Collections; import java.util.List; diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/ChargingProfilesController.java b/src/main/java/de/rwth/idsg/steve/web/controller/ChargingProfilesController.java index ea6059f1e..4103f7cc6 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/ChargingProfilesController.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/ChargingProfilesController.java @@ -28,9 +28,13 @@ import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.*; import jakarta.validation.Valid; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; /** * @author Sevket Goekay diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/LogController.java b/src/main/java/de/rwth/idsg/steve/web/controller/LogController.java index fe3161282..900627098 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/LogController.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/LogController.java @@ -25,6 +25,8 @@ import org.springframework.web.bind.annotation.RequestMapping; import jakarta.servlet.http.HttpServletResponse; + +import java.io.BufferedReader; import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; @@ -44,12 +46,14 @@ public class LogController { @GetMapping(value = "/log") public void log(HttpServletResponse response) { response.setContentType("text/plain"); + response.setCharacterEncoding(StandardCharsets.UTF_8.name()); try (PrintWriter writer = response.getWriter()) { Optional p = LogFileRetriever.INSTANCE.getPath(); if (p.isPresent()) { - Files.lines(p.get(), StandardCharsets.UTF_8) - .forEach(writer::println); + try (BufferedReader br = Files.newBufferedReader(p.get(), StandardCharsets.UTF_8)) { + br.lines().forEach(writer::println); + } } else { writer.write(LogFileRetriever.INSTANCE.getErrorMessage()); } diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/Ocpp12Controller.java b/src/main/java/de/rwth/idsg/steve/web/controller/Ocpp12Controller.java index 811ad012a..aac9e8dd2 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/Ocpp12Controller.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/Ocpp12Controller.java @@ -37,9 +37,12 @@ import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.*; import jakarta.validation.Valid; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; import java.util.Map; diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/Ocpp15Controller.java b/src/main/java/de/rwth/idsg/steve/web/controller/Ocpp15Controller.java index df34c8c73..fd56b917f 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/Ocpp15Controller.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/Ocpp15Controller.java @@ -33,9 +33,12 @@ import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.*; import jakarta.validation.Valid; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; import java.util.Map; diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/Ocpp16Controller.java b/src/main/java/de/rwth/idsg/steve/web/controller/Ocpp16Controller.java index 618f51a37..e276a86cc 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/Ocpp16Controller.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/Ocpp16Controller.java @@ -35,9 +35,12 @@ import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.*; import jakarta.validation.Valid; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; import java.util.Arrays; import java.util.Collections; diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/ReservationsController.java b/src/main/java/de/rwth/idsg/steve/web/controller/ReservationsController.java index 1776d51b3..d48fe4fa5 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/ReservationsController.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/ReservationsController.java @@ -136,7 +136,7 @@ public String getReservations(Model model) { @GetMapping(value = RESERVATIONS_QUERY_PATH) public String getReservationsQuery(@Valid @ModelAttribute(PARAMS) ReservationQueryForm params, - BindingResult result, Model model) throws IOException { + BindingResult result, Model model) { if (!result.hasErrors()) { model.addAttribute("reservList", reservationsService.getReservations(params)); } diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/TasksController.java b/src/main/java/de/rwth/idsg/steve/web/controller/TasksController.java index 67e10acb7..42836af05 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/TasksController.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/TasksController.java @@ -28,7 +28,10 @@ import ocpp.cp._2015._10.GetCompositeScheduleResponse; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; /** * @author Sevket Goekay @@ -61,20 +64,20 @@ public String getOverview(Model model) { @PostMapping(params = "finished") public String clearFinished(Model model) { taskStore.clearFinished(); - return getOverview(model); + return "redirect:/manager/operations/tasks"; } @PostMapping(params = "unfinished") public String clearUnfinished(Model model) { taskStore.clearUnfinished(); - return getOverview(model); + return "redirect:/manager/operations/tasks"; } @GetMapping(value = TASK_ID_PATH) public String getTaskDetails(@PathVariable("taskId") Integer taskId, Model model) { - var r = taskStore.get(taskId); + var task = taskStore.get(taskId); model.addAttribute("taskId", taskId); - model.addAttribute("task", r); + model.addAttribute("task", task); return "taskResult"; } @@ -83,19 +86,19 @@ public String getDetailsForChargeBox(@PathVariable("taskId") Integer taskId, @PathVariable("chargeBoxId") String chargeBoxId, Model model) { - var r = taskStore.get(taskId); + var task = taskStore.get(taskId); - if (r instanceof GetCompositeScheduleTask) { - return processForGetCompositeScheduleTask((GetCompositeScheduleTask) r, chargeBoxId, model); - } else if (r instanceof GetConfigurationTask) { - return processForGetConfigurationTask((GetConfigurationTask) r, chargeBoxId, model); - } else { - throw new SteveException("Task not found"); + if (task instanceof GetCompositeScheduleTask) { + return processForGetCompositeScheduleTask((GetCompositeScheduleTask) task, chargeBoxId, model); } + if (task instanceof GetConfigurationTask) { + return processForGetConfigurationTask((GetConfigurationTask) task, chargeBoxId, model); + } + throw new SteveException("Task not found"); } - private static String processForGetCompositeScheduleTask(GetCompositeScheduleTask k, String chargeBoxId, Model model) { - RequestResult result = extractResult(k, chargeBoxId); + private static String processForGetCompositeScheduleTask(GetCompositeScheduleTask task, String chargeBoxId, Model model) { + RequestResult result = extractResult(task, chargeBoxId); GetCompositeScheduleResponse response = result.getDetails(); model.addAttribute("chargeBoxId", chargeBoxId); @@ -115,7 +118,7 @@ private static String processForGetConfigurationTask(GetConfigurationTask k, Str private static RequestResult extractResult(CommunicationTask task, String chargeBoxId) { RequestResult result = task.getResultMap().get(chargeBoxId); if (result == null) { - throw new SteveException("Result not found"); + throw new SteveException("Result not found for chargeBoxId '" + chargeBoxId + "'"); } return result; } diff --git a/src/main/java/de/rwth/idsg/steve/web/dto/ReservationForm.java b/src/main/java/de/rwth/idsg/steve/web/dto/ReservationForm.java index c16ecd28f..ac439f0cc 100644 --- a/src/main/java/de/rwth/idsg/steve/web/dto/ReservationForm.java +++ b/src/main/java/de/rwth/idsg/steve/web/dto/ReservationForm.java @@ -18,26 +18,27 @@ */ package de.rwth.idsg.steve.web.dto; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; import lombok.Getter; import lombok.Setter; import lombok.ToString; import jakarta.validation.constraints.Future; -import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import org.joda.time.DateTime; @Getter @Setter -@ToString public class ReservationForm { - @NotEmpty(message = "ID Tag is required") + @NotBlank(message = "ID Tag is required") private String idTag; - @NotEmpty(message = "ChargeBox ID is required") + @NotBlank(message = "ChargeBox ID is required") private String chargeBoxId; @NotNull(message = "Connector ID is required") + @Min(value = 0, message = "Connector ID must be >= 0") private Integer connectorId; @NotNull(message = "Start timestamp is required") diff --git a/src/test/java/de/rwth/idsg/steve/web/api/AbstractControllerTest.java b/src/test/java/de/rwth/idsg/steve/web/api/AbstractControllerTest.java index 562098b7d..cb31a881f 100644 --- a/src/test/java/de/rwth/idsg/steve/web/api/AbstractControllerTest.java +++ b/src/test/java/de/rwth/idsg/steve/web/api/AbstractControllerTest.java @@ -22,6 +22,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import org.jetbrains.annotations.NotNull; +import org.springframework.http.MediaType; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.test.web.servlet.MockMvc; @@ -31,8 +32,6 @@ public abstract class AbstractControllerTest { - private static final String CONTENT_TYPE = "application/json"; - protected final ObjectMapper objectMapper; AbstractControllerTest() { @@ -52,7 +51,7 @@ public abstract class AbstractControllerTest { protected MockMvc buildMockMvc(StandaloneMockMvcBuilder builder) { return builder.setControllerAdvice(new ApiControllerAdvice()) .setMessageConverters(new MappingJackson2HttpMessageConverter(objectMapper)) - .alwaysExpect(content().contentType(CONTENT_TYPE)) + .alwaysExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) .build(); } } diff --git a/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java b/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java index a1790f6cc..b8e175ae6 100644 --- a/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java +++ b/src/test/java/de/rwth/idsg/steve/web/api/ChargePointsRestControllerTest.java @@ -38,6 +38,7 @@ import static org.hamcrest.Matchers.hasSize; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; @@ -85,7 +86,7 @@ public void testGet_withOneResult() throws Exception { @Test @DisplayName("GET one: Entity not found, expected 404") public void testGetOne_notFound() throws Exception { - when(chargePointsService.getDetails(any(Integer.class))).thenThrow(new NotFoundException("")); + when(chargePointsService.getDetails(anyInt())).thenThrow(new NotFoundException("")); mockMvc.perform(get("/api/v1/chargeboxes/1")) .andExpect(status().isNotFound()); diff --git a/src/test/java/de/rwth/idsg/steve/web/api/OcppTagsRestControllerTest.java b/src/test/java/de/rwth/idsg/steve/web/api/OcppTagsRestControllerTest.java index 0ec8524af..7279e5525 100644 --- a/src/test/java/de/rwth/idsg/steve/web/api/OcppTagsRestControllerTest.java +++ b/src/test/java/de/rwth/idsg/steve/web/api/OcppTagsRestControllerTest.java @@ -486,7 +486,7 @@ public void test20() throws Exception { } @Test - @DisplayName("GET all: Query param 'inTransaction' is translated correctly, while others are defaulted") + @DisplayName("GET all: Query param 'blocked' is translated correctly, while others are defaulted") public void test21() throws Exception { // given ArgumentCaptor formToCapture = ArgumentCaptor.forClass(OcppTagQueryForm.OcppTagQueryFormForApi.class); diff --git a/src/test/java/de/rwth/idsg/steve/web/api/ReservationRestControllerTest.java b/src/test/java/de/rwth/idsg/steve/web/api/ReservationRestControllerTest.java index a8458da57..fab3c3fb6 100644 --- a/src/test/java/de/rwth/idsg/steve/web/api/ReservationRestControllerTest.java +++ b/src/test/java/de/rwth/idsg/steve/web/api/ReservationRestControllerTest.java @@ -24,11 +24,16 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import java.util.Collections; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @ExtendWith(MockitoExtension.class) class ReservationRestControllerTest extends AbstractControllerTest { @@ -45,7 +50,12 @@ public void setup() { @Test void testGetReservations() throws Exception { + when(reservationsService.getReservations(any())).thenReturn(Collections.emptyList()); + mockMvc.perform(get("/api/reservations")) - .andExpect(status().isOk()); + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$").isArray()) + .andExpect(jsonPath("$").isEmpty()); } } diff --git a/src/test/java/de/rwth/idsg/steve/web/api/TransactionRestControllerTest.java b/src/test/java/de/rwth/idsg/steve/web/api/TransactionRestControllerTest.java index 9d5ec9f24..2dac9cd55 100644 --- a/src/test/java/de/rwth/idsg/steve/web/api/TransactionRestControllerTest.java +++ b/src/test/java/de/rwth/idsg/steve/web/api/TransactionRestControllerTest.java @@ -184,7 +184,6 @@ public void test8() throws Exception { .andExpect(jsonPath("$[0].id").value("1")) .andExpect(jsonPath("$[0].chargeBoxId").value("cb-2")) .andExpect(jsonPath("$[0].ocppIdTag").value("id-3")) - .andExpect(jsonPath("$[0].ocppIdTag").value("id-3")) .andExpect(jsonPath("$[0].startTimestamp").value(start.toString())) .andExpect(jsonPath("$[0].startTimestampFormatted").doesNotExist()) .andExpect(jsonPath("$[0].stopTimestamp").value(stop.toString())) diff --git a/src/test/java/de/rwth/idsg/steve/web/api/UsersRestControllerTest.java b/src/test/java/de/rwth/idsg/steve/web/api/UsersRestControllerTest.java index 724dcc219..e2bf51870 100644 --- a/src/test/java/de/rwth/idsg/steve/web/api/UsersRestControllerTest.java +++ b/src/test/java/de/rwth/idsg/steve/web/api/UsersRestControllerTest.java @@ -24,10 +24,16 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import java.util.Collections; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @ExtendWith(MockitoExtension.class) @@ -45,7 +51,9 @@ public void setup() { @Test void testGetUsers() throws Exception { + when(usersService.getOverview(any())).thenReturn(Collections.emptyList()); mockMvc.perform(get("/api/users")) - .andExpect(status().isOk()); + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)); } } From e8f4474c9ff9602c3be29657f4d952b8702b2224 Mon Sep 17 00:00:00 2001 From: Julien Herr Date: Tue, 19 Aug 2025 21:42:57 +0200 Subject: [PATCH 3/4] fix: remove bean cycling dependency --- .../idsg/steve/config/OcppConfiguration.java | 2 +- .../steve/config/WebSocketConfiguration.java | 20 ++-- .../CentralSystemService12_SoapServer.java | 5 +- .../CentralSystemService15_SoapServer.java | 4 +- .../CentralSystemService16_SoapServer.java | 5 +- .../ocpp/soap/MessageHeaderInterceptor.java | 24 +++-- .../ocpp/ws/AbstractWebSocketEndpoint.java | 11 ++- .../ocpp/ws/ConcurrentWebSocketHandler.java | 6 +- .../ws/OcppWebSocketHandshakeHandler.java | 7 +- .../ws/ocpp12/Ocpp12WebSocketEndpoint.java | 18 +++- .../ws/ocpp15/Ocpp15WebSocketEndpoint.java | 19 +++- .../ws/ocpp16/Ocpp16WebSocketEndpoint.java | 19 +++- .../CentralSystemService16_Service.java | 4 +- .../service/ChargePointHelperService.java | 71 ++------------ .../ChargePointRegistrationService.java | 97 +++++++++++++++++++ .../controller/ChargePointsController.java | 12 +-- 16 files changed, 206 insertions(+), 118 deletions(-) create mode 100644 src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.java diff --git a/src/main/java/de/rwth/idsg/steve/config/OcppConfiguration.java b/src/main/java/de/rwth/idsg/steve/config/OcppConfiguration.java index c21af9d05..e3ef84add 100644 --- a/src/main/java/de/rwth/idsg/steve/config/OcppConfiguration.java +++ b/src/main/java/de/rwth/idsg/steve/config/OcppConfiguration.java @@ -62,7 +62,7 @@ public class OcppConfiguration { @Autowired private ocpp.cs._2015._10.CentralSystemService ocpp16Server; @Autowired - @Qualifier("MessageHeaderInterceptor") + @Qualifier("messageHeaderInterceptor") private PhaseInterceptor messageHeaderInterceptor; @PostConstruct diff --git a/src/main/java/de/rwth/idsg/steve/config/WebSocketConfiguration.java b/src/main/java/de/rwth/idsg/steve/config/WebSocketConfiguration.java index e93d64a63..b2fd17e40 100644 --- a/src/main/java/de/rwth/idsg/steve/config/WebSocketConfiguration.java +++ b/src/main/java/de/rwth/idsg/steve/config/WebSocketConfiguration.java @@ -23,9 +23,9 @@ import de.rwth.idsg.steve.ocpp.ws.ocpp12.Ocpp12WebSocketEndpoint; import de.rwth.idsg.steve.ocpp.ws.ocpp15.Ocpp15WebSocketEndpoint; import de.rwth.idsg.steve.ocpp.ws.ocpp16.Ocpp16WebSocketEndpoint; -import de.rwth.idsg.steve.service.ChargePointHelperService; +import de.rwth.idsg.steve.service.ChargePointRegistrationService; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; @@ -33,7 +33,6 @@ import org.springframework.web.socket.server.support.DefaultHandshakeHandler; import java.time.Duration; -import java.util.concurrent.TimeUnit; /** * @author Sevket Goekay @@ -42,26 +41,27 @@ @EnableWebSocket @Configuration @Slf4j +@RequiredArgsConstructor public class WebSocketConfiguration implements WebSocketConfigurer { - @Autowired private ChargePointHelperService chargePointHelperService; - - @Autowired private Ocpp12WebSocketEndpoint ocpp12WebSocketEndpoint; - @Autowired private Ocpp15WebSocketEndpoint ocpp15WebSocketEndpoint; - @Autowired private Ocpp16WebSocketEndpoint ocpp16WebSocketEndpoint; - public static final String PATH_INFIX = "/websocket/CentralSystemService/"; public static final Duration PING_INTERVAL = Duration.ofMinutes(15); public static final Duration IDLE_TIMEOUT = Duration.ofHours(2); public static final int MAX_MSG_SIZE = 8_388_608; // 8 MB for max message size + private final ChargePointRegistrationService chargePointRegistrationService; + + private final Ocpp12WebSocketEndpoint ocpp12WebSocketEndpoint; + private final Ocpp15WebSocketEndpoint ocpp15WebSocketEndpoint; + private final Ocpp16WebSocketEndpoint ocpp16WebSocketEndpoint; + @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { OcppWebSocketHandshakeHandler handshakeHandler = new OcppWebSocketHandshakeHandler( new DefaultHandshakeHandler(), Lists.newArrayList(ocpp16WebSocketEndpoint, ocpp15WebSocketEndpoint, ocpp12WebSocketEndpoint), - chargePointHelperService + chargePointRegistrationService ); registry.addHandler(handshakeHandler.getDummyWebSocketHandler(), PATH_INFIX + "*") diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/soap/CentralSystemService12_SoapServer.java b/src/main/java/de/rwth/idsg/steve/ocpp/soap/CentralSystemService12_SoapServer.java index be9ab4cd8..f961d9515 100644 --- a/src/main/java/de/rwth/idsg/steve/ocpp/soap/CentralSystemService12_SoapServer.java +++ b/src/main/java/de/rwth/idsg/steve/ocpp/soap/CentralSystemService12_SoapServer.java @@ -24,6 +24,7 @@ import de.rwth.idsg.steve.ocpp.converter.Server12to15Impl; import de.rwth.idsg.steve.ocpp.converter.Server15to16Impl; import de.rwth.idsg.steve.service.CentralSystemService16_Service; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import ocpp.cs._2010._08.AuthorizeRequest; import ocpp.cs._2010._08.AuthorizeResponse; @@ -44,7 +45,6 @@ import ocpp.cs._2010._08.StatusNotificationResponse; import ocpp.cs._2010._08.StopTransactionRequest; import ocpp.cs._2010._08.StopTransactionResponse; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import jakarta.jws.WebService; @@ -69,9 +69,10 @@ portName = "CentralSystemServiceSoap12", targetNamespace = "urn://Ocpp/Cs/2010/08/", endpointInterface = "ocpp.cs._2010._08.CentralSystemService") +@RequiredArgsConstructor public class CentralSystemService12_SoapServer implements CentralSystemService { - @Autowired private CentralSystemService16_Service service; + private final CentralSystemService16_Service service; public BootNotificationResponse bootNotificationWithTransport(BootNotificationRequest parameters, String chargeBoxIdentity, OcppProtocol protocol) { diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/soap/CentralSystemService15_SoapServer.java b/src/main/java/de/rwth/idsg/steve/ocpp/soap/CentralSystemService15_SoapServer.java index 034f8f32f..176b7bb4d 100644 --- a/src/main/java/de/rwth/idsg/steve/ocpp/soap/CentralSystemService15_SoapServer.java +++ b/src/main/java/de/rwth/idsg/steve/ocpp/soap/CentralSystemService15_SoapServer.java @@ -23,6 +23,7 @@ import de.rwth.idsg.steve.ocpp.converter.Convert; import de.rwth.idsg.steve.ocpp.converter.Server15to16Impl; import de.rwth.idsg.steve.service.CentralSystemService16_Service; +import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import ocpp.cs._2012._06.AuthorizeRequest; import ocpp.cs._2012._06.AuthorizeResponse; @@ -70,9 +71,10 @@ portName = "CentralSystemServiceSoap12", targetNamespace = "urn://Ocpp/Cs/2012/06/", endpointInterface = "ocpp.cs._2012._06.CentralSystemService") +@AllArgsConstructor public class CentralSystemService15_SoapServer implements CentralSystemService { - @Autowired private CentralSystemService16_Service service; + private final CentralSystemService16_Service service; public BootNotificationResponse bootNotificationWithTransport(BootNotificationRequest parameters, String chargeBoxIdentity, OcppProtocol protocol) { diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/soap/CentralSystemService16_SoapServer.java b/src/main/java/de/rwth/idsg/steve/ocpp/soap/CentralSystemService16_SoapServer.java index c32cd0dd1..337f5242b 100644 --- a/src/main/java/de/rwth/idsg/steve/ocpp/soap/CentralSystemService16_SoapServer.java +++ b/src/main/java/de/rwth/idsg/steve/ocpp/soap/CentralSystemService16_SoapServer.java @@ -21,6 +21,7 @@ import de.rwth.idsg.steve.ocpp.OcppProtocol; import de.rwth.idsg.steve.ocpp.OcppVersion; import de.rwth.idsg.steve.service.CentralSystemService16_Service; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import ocpp.cs._2015._10.AuthorizeRequest; import ocpp.cs._2015._10.AuthorizeResponse; @@ -43,7 +44,6 @@ import ocpp.cs._2015._10.StatusNotificationResponse; import ocpp.cs._2015._10.StopTransactionRequest; import ocpp.cs._2015._10.StopTransactionResponse; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import jakarta.jws.WebService; @@ -67,9 +67,10 @@ portName = "CentralSystemServiceSoap12", targetNamespace = "urn://Ocpp/Cs/2015/10/", endpointInterface = "ocpp.cs._2015._10.CentralSystemService") +@RequiredArgsConstructor public class CentralSystemService16_SoapServer implements CentralSystemService { - @Autowired private CentralSystemService16_Service service; + private final CentralSystemService16_Service service; public BootNotificationResponse bootNotificationWithTransport(BootNotificationRequest parameters, String chargeBoxIdentity, OcppProtocol protocol) { diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/soap/MessageHeaderInterceptor.java b/src/main/java/de/rwth/idsg/steve/ocpp/soap/MessageHeaderInterceptor.java index d0b3040aa..f93e4f3dc 100644 --- a/src/main/java/de/rwth/idsg/steve/ocpp/soap/MessageHeaderInterceptor.java +++ b/src/main/java/de/rwth/idsg/steve/ocpp/soap/MessageHeaderInterceptor.java @@ -22,7 +22,7 @@ import de.rwth.idsg.steve.ocpp.OcppProtocol; import de.rwth.idsg.steve.repository.OcppServerRepository; import de.rwth.idsg.steve.repository.impl.ChargePointRepositoryImpl; -import de.rwth.idsg.steve.service.ChargePointHelperService; +import de.rwth.idsg.steve.service.ChargePointRegistrationService; import lombok.extern.slf4j.Slf4j; import ocpp.cs._2015._10.RegistrationStatus; import org.apache.cxf.binding.soap.Soap12; @@ -37,7 +37,6 @@ import org.apache.cxf.ws.addressing.AddressingProperties; import org.apache.cxf.ws.addressing.ContextUtils; import org.apache.cxf.ws.addressing.EndpointReferenceType; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.xml.namespace.QName; @@ -57,18 +56,25 @@ * @since 15.06.2015 */ @Slf4j -@Component("MessageHeaderInterceptor") +@Component("messageHeaderInterceptor") public class MessageHeaderInterceptor extends AbstractPhaseInterceptor { - @Autowired private OcppServerRepository ocppServerRepository; - @Autowired private ChargePointHelperService chargePointHelperService; - @Autowired private DelegatingTaskExecutor asyncTaskExecutor; - private static final String BOOT_OPERATION_NAME = "BootNotification"; private static final String CHARGEBOX_ID_HEADER = "ChargeBoxIdentity"; - public MessageHeaderInterceptor() { + private final OcppServerRepository ocppServerRepository; + private final ChargePointRegistrationService chargePointRegistrationService; + private final DelegatingTaskExecutor asyncTaskExecutor; + + public MessageHeaderInterceptor( + OcppServerRepository ocppServerRepository, + ChargePointRegistrationService chargePointRegistrationService, + DelegatingTaskExecutor asyncTaskExecutor + ) { super(Phase.PRE_INVOKE); + this.ocppServerRepository = ocppServerRepository; + this.chargePointRegistrationService = chargePointRegistrationService; + this.asyncTaskExecutor = asyncTaskExecutor; } @Override @@ -82,7 +88,7 @@ public void handleMessage(Message message) throws Fault { QName opName = message.getExchange().getBindingOperationInfo().getOperationInfo().getName(); if (!BOOT_OPERATION_NAME.equals(opName.getLocalPart())) { - Optional status = chargePointHelperService.getRegistrationStatus(chargeBoxId); + Optional status = chargePointRegistrationService.getRegistrationStatus(chargeBoxId); boolean allow = status.isPresent() && status.get() != RegistrationStatus.REJECTED; if (!allow) { throw createAuthFault(opName); diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/ws/AbstractWebSocketEndpoint.java b/src/main/java/de/rwth/idsg/steve/ocpp/ws/AbstractWebSocketEndpoint.java index 8a55a0079..0b2da0fd6 100644 --- a/src/main/java/de/rwth/idsg/steve/ocpp/ws/AbstractWebSocketEndpoint.java +++ b/src/main/java/de/rwth/idsg/steve/ocpp/ws/AbstractWebSocketEndpoint.java @@ -29,8 +29,8 @@ import de.rwth.idsg.steve.repository.OcppServerRepository; import de.rwth.idsg.steve.service.notification.OcppStationWebSocketConnected; import de.rwth.idsg.steve.service.notification.OcppStationWebSocketDisconnected; +import lombok.RequiredArgsConstructor; import org.joda.time.DateTime; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; import org.springframework.web.socket.BinaryMessage; import org.springframework.web.socket.CloseStatus; @@ -53,12 +53,13 @@ * @author Sevket Goekay * @since 17.03.2015 */ +@RequiredArgsConstructor public abstract class AbstractWebSocketEndpoint extends ConcurrentWebSocketHandler implements SubProtocolCapable { - @Autowired private DelegatingTaskScheduler asyncTaskScheduler; - @Autowired private OcppServerRepository ocppServerRepository; - @Autowired private FutureResponseContextStore futureResponseContextStore; - @Autowired private ApplicationEventPublisher applicationEventPublisher; + private final DelegatingTaskScheduler asyncTaskScheduler; + private final OcppServerRepository ocppServerRepository; + private final FutureResponseContextStore futureResponseContextStore; + private final ApplicationEventPublisher applicationEventPublisher; public static final String CHARGEBOX_ID_KEY = "CHARGEBOX_ID_KEY"; diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/ws/ConcurrentWebSocketHandler.java b/src/main/java/de/rwth/idsg/steve/ocpp/ws/ConcurrentWebSocketHandler.java index 024ee9241..429a5d304 100644 --- a/src/main/java/de/rwth/idsg/steve/ocpp/ws/ConcurrentWebSocketHandler.java +++ b/src/main/java/de/rwth/idsg/steve/ocpp/ws/ConcurrentWebSocketHandler.java @@ -35,8 +35,8 @@ */ public abstract class ConcurrentWebSocketHandler implements WebSocketHandler { - private static final int sendTimeLimit = (int) TimeUnit.SECONDS.toMillis(10); - private static final int bufferSizeLimit = 5 * WebSocketConfiguration.MAX_MSG_SIZE; + private static final int SEND_TIME_LIMIT = (int) TimeUnit.SECONDS.toMillis(10); + private static final int BUFFER_SIZE_LIMIT = 5 * WebSocketConfiguration.MAX_MSG_SIZE; private final Map sessions = new ConcurrentHashMap<>(); @@ -61,7 +61,7 @@ public void afterConnectionClosed(WebSocketSession session, CloseStatus closeSta } private ConcurrentWebSocketSessionDecorator internalGet(WebSocketSession session) { - return sessions.computeIfAbsent(session.getId(), s -> new ConcurrentWebSocketSessionDecorator(session, sendTimeLimit, bufferSizeLimit)); + return sessions.computeIfAbsent(session.getId(), s -> new ConcurrentWebSocketSessionDecorator(session, SEND_TIME_LIMIT, BUFFER_SIZE_LIMIT)); } // ------------------------------------------------------------------------- diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java b/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java index f0796ae5b..963ff23ff 100644 --- a/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java +++ b/src/main/java/de/rwth/idsg/steve/ocpp/ws/OcppWebSocketHandshakeHandler.java @@ -18,8 +18,7 @@ */ package de.rwth.idsg.steve.ocpp.ws; -import de.rwth.idsg.steve.config.WebSocketConfiguration; -import de.rwth.idsg.steve.service.ChargePointHelperService; +import de.rwth.idsg.steve.service.ChargePointRegistrationService; import de.rwth.idsg.steve.web.validation.ChargeBoxIdValidator; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -53,7 +52,7 @@ public class OcppWebSocketHandshakeHandler implements HandshakeHandler { private final DefaultHandshakeHandler delegate; private final List endpoints; - private final ChargePointHelperService chargePointHelperService; + private final ChargePointRegistrationService chargePointRegistrationService; /** * We need some WebSocketHandler just for Spring to register it for the path. We will not use it for the actual @@ -80,7 +79,7 @@ public boolean doHandshake(ServerHttpRequest request, ServerHttpResponse respons return false; } - Optional status = chargePointHelperService.getRegistrationStatus(chargeBoxId); + Optional status = chargePointRegistrationService.getRegistrationStatus(chargeBoxId); // Allow connections, if station is in db (registration_status field from db does not matter) boolean allowConnection = status.isPresent(); diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/ws/ocpp12/Ocpp12WebSocketEndpoint.java b/src/main/java/de/rwth/idsg/steve/ocpp/ws/ocpp12/Ocpp12WebSocketEndpoint.java index 697443877..bbef07b3f 100644 --- a/src/main/java/de/rwth/idsg/steve/ocpp/ws/ocpp12/Ocpp12WebSocketEndpoint.java +++ b/src/main/java/de/rwth/idsg/steve/ocpp/ws/ocpp12/Ocpp12WebSocketEndpoint.java @@ -20,6 +20,7 @@ import de.rwth.idsg.ocpp.jaxb.RequestType; import de.rwth.idsg.ocpp.jaxb.ResponseType; +import de.rwth.idsg.steve.config.DelegatingTaskScheduler; import de.rwth.idsg.steve.ocpp.OcppProtocol; import de.rwth.idsg.steve.ocpp.OcppVersion; import de.rwth.idsg.steve.ocpp.soap.CentralSystemService12_SoapServer; @@ -28,6 +29,7 @@ import de.rwth.idsg.steve.ocpp.ws.pipeline.AbstractCallHandler; import de.rwth.idsg.steve.ocpp.ws.pipeline.Deserializer; import de.rwth.idsg.steve.ocpp.ws.pipeline.IncomingPipeline; +import de.rwth.idsg.steve.repository.OcppServerRepository; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import ocpp.cs._2010._08.AuthorizeRequest; @@ -39,7 +41,7 @@ import ocpp.cs._2010._08.StartTransactionRequest; import ocpp.cs._2010._08.StatusNotificationRequest; import ocpp.cs._2010._08.StopTransactionRequest; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Component; import jakarta.annotation.PostConstruct; @@ -51,8 +53,18 @@ @Component public class Ocpp12WebSocketEndpoint extends AbstractWebSocketEndpoint { - @Autowired private CentralSystemService12_SoapServer server; - @Autowired private FutureResponseContextStore futureResponseContextStore; + private final CentralSystemService12_SoapServer server; + private final FutureResponseContextStore futureResponseContextStore; + + public Ocpp12WebSocketEndpoint(DelegatingTaskScheduler asyncTaskScheduler, OcppServerRepository ocppServerRepository, + FutureResponseContextStore futureResponseContextStore, + ApplicationEventPublisher applicationEventPublisher, + CentralSystemService12_SoapServer server + ) { + super(asyncTaskScheduler, ocppServerRepository, futureResponseContextStore, applicationEventPublisher); + this.server = server; + this.futureResponseContextStore = futureResponseContextStore; + } @PostConstruct public void init() { diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/ws/ocpp15/Ocpp15WebSocketEndpoint.java b/src/main/java/de/rwth/idsg/steve/ocpp/ws/ocpp15/Ocpp15WebSocketEndpoint.java index 93b7229dc..8fa9e16f8 100644 --- a/src/main/java/de/rwth/idsg/steve/ocpp/ws/ocpp15/Ocpp15WebSocketEndpoint.java +++ b/src/main/java/de/rwth/idsg/steve/ocpp/ws/ocpp15/Ocpp15WebSocketEndpoint.java @@ -20,6 +20,7 @@ import de.rwth.idsg.ocpp.jaxb.RequestType; import de.rwth.idsg.ocpp.jaxb.ResponseType; +import de.rwth.idsg.steve.config.DelegatingTaskScheduler; import de.rwth.idsg.steve.ocpp.OcppProtocol; import de.rwth.idsg.steve.ocpp.OcppVersion; import de.rwth.idsg.steve.ocpp.soap.CentralSystemService15_SoapServer; @@ -28,6 +29,7 @@ import de.rwth.idsg.steve.ocpp.ws.pipeline.AbstractCallHandler; import de.rwth.idsg.steve.ocpp.ws.pipeline.Deserializer; import de.rwth.idsg.steve.ocpp.ws.pipeline.IncomingPipeline; +import de.rwth.idsg.steve.repository.OcppServerRepository; import lombok.RequiredArgsConstructor; import ocpp.cs._2012._06.AuthorizeRequest; import ocpp.cs._2012._06.BootNotificationRequest; @@ -39,7 +41,7 @@ import ocpp.cs._2012._06.StartTransactionRequest; import ocpp.cs._2012._06.StatusNotificationRequest; import ocpp.cs._2012._06.StopTransactionRequest; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Component; import jakarta.annotation.PostConstruct; @@ -51,8 +53,19 @@ @Component public class Ocpp15WebSocketEndpoint extends AbstractWebSocketEndpoint { - @Autowired private CentralSystemService15_SoapServer server; - @Autowired private FutureResponseContextStore futureResponseContextStore; + private final CentralSystemService15_SoapServer server; + private final FutureResponseContextStore futureResponseContextStore; + + public Ocpp15WebSocketEndpoint(DelegatingTaskScheduler asyncTaskScheduler, + OcppServerRepository ocppServerRepository, + FutureResponseContextStore futureResponseContextStore, + ApplicationEventPublisher applicationEventPublisher, + CentralSystemService15_SoapServer server + ) { + super(asyncTaskScheduler, ocppServerRepository, futureResponseContextStore, applicationEventPublisher); + this.server = server; + this.futureResponseContextStore = futureResponseContextStore; + } @PostConstruct public void init() { diff --git a/src/main/java/de/rwth/idsg/steve/ocpp/ws/ocpp16/Ocpp16WebSocketEndpoint.java b/src/main/java/de/rwth/idsg/steve/ocpp/ws/ocpp16/Ocpp16WebSocketEndpoint.java index a4d5e1534..e6705eaa9 100644 --- a/src/main/java/de/rwth/idsg/steve/ocpp/ws/ocpp16/Ocpp16WebSocketEndpoint.java +++ b/src/main/java/de/rwth/idsg/steve/ocpp/ws/ocpp16/Ocpp16WebSocketEndpoint.java @@ -20,6 +20,7 @@ import de.rwth.idsg.ocpp.jaxb.RequestType; import de.rwth.idsg.ocpp.jaxb.ResponseType; +import de.rwth.idsg.steve.config.DelegatingTaskScheduler; import de.rwth.idsg.steve.ocpp.OcppProtocol; import de.rwth.idsg.steve.ocpp.OcppVersion; import de.rwth.idsg.steve.ocpp.soap.CentralSystemService16_SoapServer; @@ -28,6 +29,7 @@ import de.rwth.idsg.steve.ocpp.ws.pipeline.AbstractCallHandler; import de.rwth.idsg.steve.ocpp.ws.pipeline.Deserializer; import de.rwth.idsg.steve.ocpp.ws.pipeline.IncomingPipeline; +import de.rwth.idsg.steve.repository.OcppServerRepository; import lombok.RequiredArgsConstructor; import ocpp.cs._2015._10.AuthorizeRequest; import ocpp.cs._2015._10.BootNotificationRequest; @@ -39,7 +41,7 @@ import ocpp.cs._2015._10.StartTransactionRequest; import ocpp.cs._2015._10.StatusNotificationRequest; import ocpp.cs._2015._10.StopTransactionRequest; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Component; import jakarta.annotation.PostConstruct; @@ -51,8 +53,19 @@ @Component public class Ocpp16WebSocketEndpoint extends AbstractWebSocketEndpoint { - @Autowired private CentralSystemService16_SoapServer server; - @Autowired private FutureResponseContextStore futureResponseContextStore; + private final CentralSystemService16_SoapServer server; + private final FutureResponseContextStore futureResponseContextStore; + + public Ocpp16WebSocketEndpoint(DelegatingTaskScheduler asyncTaskScheduler, + OcppServerRepository ocppServerRepository, + FutureResponseContextStore futureResponseContextStore, + ApplicationEventPublisher applicationEventPublisher, + CentralSystemService16_SoapServer server + ) { + super(asyncTaskScheduler, ocppServerRepository, futureResponseContextStore, applicationEventPublisher); + this.server = server; + this.futureResponseContextStore = futureResponseContextStore; + } @PostConstruct public void init() { diff --git a/src/main/java/de/rwth/idsg/steve/service/CentralSystemService16_Service.java b/src/main/java/de/rwth/idsg/steve/service/CentralSystemService16_Service.java index ec80d0ece..0be3c2fbe 100644 --- a/src/main/java/de/rwth/idsg/steve/service/CentralSystemService16_Service.java +++ b/src/main/java/de/rwth/idsg/steve/service/CentralSystemService16_Service.java @@ -78,12 +78,12 @@ public class CentralSystemService16_Service { private final OcppTagsService ocppTagsService; private final ApplicationEventPublisher applicationEventPublisher; - private final ChargePointHelperService chargePointHelperService; + private final ChargePointRegistrationService chargePointRegistrationService; public BootNotificationResponse bootNotification(BootNotificationRequest parameters, String chargeBoxIdentity, OcppProtocol ocppProtocol) { - Optional status = chargePointHelperService.getRegistrationStatus(chargeBoxIdentity); + Optional status = chargePointRegistrationService.getRegistrationStatus(chargeBoxIdentity); applicationEventPublisher.publishEvent(new OccpStationBooted(chargeBoxIdentity, status)); DateTime now = DateTime.now(); diff --git a/src/main/java/de/rwth/idsg/steve/service/ChargePointHelperService.java b/src/main/java/de/rwth/idsg/steve/service/ChargePointHelperService.java index 28c8bc7b4..aa8508eb0 100644 --- a/src/main/java/de/rwth/idsg/steve/service/ChargePointHelperService.java +++ b/src/main/java/de/rwth/idsg/steve/service/ChargePointHelperService.java @@ -18,7 +18,6 @@ */ package de.rwth.idsg.steve.service; -import com.google.common.util.concurrent.Striped; import de.rwth.idsg.steve.ocpp.OcppProtocol; import de.rwth.idsg.steve.ocpp.OcppTransport; import de.rwth.idsg.steve.ocpp.OcppVersion; @@ -37,10 +36,10 @@ import de.rwth.idsg.steve.web.dto.ConnectorStatusForm; import de.rwth.idsg.steve.web.dto.OcppJsonStatus; import de.rwth.idsg.steve.web.dto.Statistics; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import ocpp.cs._2015._10.RegistrationStatus; import org.joda.time.DateTime; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; @@ -54,7 +53,6 @@ import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.concurrent.locks.Lock; import java.util.stream.Collectors; import static de.rwth.idsg.steve.SteveConfiguration.CONFIG; @@ -65,36 +63,18 @@ */ @Slf4j @Service +@RequiredArgsConstructor public class ChargePointHelperService { - private final boolean autoRegisterUnknownStations = CONFIG.getOcpp().isAutoRegisterUnknownStations(); - private final Striped isRegisteredLocks = Striped.lock(16); - - @Autowired private GenericRepository genericRepository; + private final GenericRepository genericRepository; // SOAP-based charge points are stored in DB with an endpoint address - @Autowired private ChargePointRepository chargePointRepository; + private final ChargePointRepository chargePointRepository; // For WebSocket-based charge points, the active sessions are stored in memory - @Autowired private Ocpp12WebSocketEndpoint ocpp12WebSocketEndpoint; - @Autowired private Ocpp15WebSocketEndpoint ocpp15WebSocketEndpoint; - @Autowired private Ocpp16WebSocketEndpoint ocpp16WebSocketEndpoint; - - private final UnidentifiedIncomingObjectService unknownChargePointService = new UnidentifiedIncomingObjectService(100); - - public Optional getRegistrationStatus(String chargeBoxId) { - Lock l = isRegisteredLocks.get(chargeBoxId); - l.lock(); - try { - Optional status = getRegistrationStatusInternal(chargeBoxId); - if (status.isEmpty()) { - unknownChargePointService.processNewUnidentified(chargeBoxId); - } - return status; - } finally { - l.unlock(); - } - } + private final Ocpp12WebSocketEndpoint ocpp12WebSocketEndpoint; + private final Ocpp15WebSocketEndpoint ocpp15WebSocketEndpoint; + private final Ocpp16WebSocketEndpoint ocpp16WebSocketEndpoint; public Statistics getStats() { Statistics stats = genericRepository.getStats(); @@ -172,47 +152,10 @@ public List getChargePoints(OcppVersion version, List getUnknownChargePoints() { - return unknownChargePointService.getObjects(); - } - - public void removeUnknown(List chargeBoxIdList) { - unknownChargePointService.removeAll(chargeBoxIdList); - } - // ------------------------------------------------------------------------- // Helpers // ------------------------------------------------------------------------- - private Optional getRegistrationStatusInternal(String chargeBoxId) { - // 1. exit if already registered - Optional status = chargePointRepository.getRegistrationStatus(chargeBoxId); - if (status.isPresent()) { - try { - return Optional.ofNullable(RegistrationStatus.fromValue(status.get())); - } catch (Exception e) { - // in cases where the database entry (string) is altered, and therefore cannot be converted to enum - log.error("Exception happened", e); - return Optional.empty(); - } - } - - // 2. ok, this chargeBoxId is unknown. exit if auto-register is disabled - if (!autoRegisterUnknownStations) { - return Optional.empty(); - } - - // 3. chargeBoxId is unknown and auto-register is enabled. insert chargeBoxId - try { - chargePointRepository.addChargePointList(Collections.singletonList(chargeBoxId)); - log.warn("Auto-registered unknown chargebox '{}'", chargeBoxId); - return Optional.of(RegistrationStatus.ACCEPTED); // default db value is accepted - } catch (Exception e) { - log.error("Failed to auto-register unknown chargebox '" + chargeBoxId + "'", e); - return Optional.empty(); - } - } - private List getChargePoints(OcppProtocol protocol, List inStatusFilter, List chargeBoxIdFilter, AbstractWebSocketEndpoint jsonEndpoint) { // soap stations diff --git a/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.java b/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.java new file mode 100644 index 000000000..1046f4e4d --- /dev/null +++ b/src/main/java/de/rwth/idsg/steve/service/ChargePointRegistrationService.java @@ -0,0 +1,97 @@ +/* + * SteVe - SteckdosenVerwaltung - https://github.com/steve-community/steve + * Copyright (C) 2013-2025 SteVe Community Team + * All Rights Reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.rwth.idsg.steve.service; + +import com.google.common.util.concurrent.Striped; +import de.rwth.idsg.steve.repository.ChargePointRepository; +import de.rwth.idsg.steve.service.dto.UnidentifiedIncomingObject; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import ocpp.cs._2015._10.RegistrationStatus; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.locks.Lock; + +import static de.rwth.idsg.steve.SteveConfiguration.CONFIG; + +@Slf4j +@Service +@RequiredArgsConstructor +public class ChargePointRegistrationService { + + private final boolean autoRegisterUnknownStations = CONFIG.getOcpp().isAutoRegisterUnknownStations(); + + private final Striped isRegisteredLocks = Striped.lock(16); + private final UnidentifiedIncomingObjectService unknownChargePointService = new UnidentifiedIncomingObjectService(100); + private final ChargePointRepository chargePointRepository; + + public Optional getRegistrationStatus(String chargeBoxId) { + Lock l = isRegisteredLocks.get(chargeBoxId); + l.lock(); + try { + Optional status = getRegistrationStatusInternal(chargeBoxId); + if (status.isEmpty()) { + unknownChargePointService.processNewUnidentified(chargeBoxId); + } + return status; + } finally { + l.unlock(); + } + } + + private Optional getRegistrationStatusInternal(String chargeBoxId) { + // 1. exit if already registered + Optional status = chargePointRepository.getRegistrationStatus(chargeBoxId); + if (status.isPresent()) { + try { + return Optional.ofNullable(RegistrationStatus.fromValue(status.get())); + } catch (Exception e) { + // in cases where the database entry (string) is altered, and therefore cannot be converted to enum + log.error("Exception happened", e); + return Optional.empty(); + } + } + + // 2. ok, this chargeBoxId is unknown. exit if auto-register is disabled + if (!autoRegisterUnknownStations) { + return Optional.empty(); + } + + // 3. chargeBoxId is unknown and auto-register is enabled. insert chargeBoxId + try { + chargePointRepository.addChargePointList(Collections.singletonList(chargeBoxId)); + log.warn("Auto-registered unknown chargebox '{}'", chargeBoxId); + return Optional.of(RegistrationStatus.ACCEPTED); // default db value is accepted + } catch (Exception e) { + log.error("Failed to auto-register unknown chargebox '" + chargeBoxId + "'", e); + return Optional.empty(); + } + } + + public List getUnknownChargePoints() { + return unknownChargePointService.getObjects(); + } + + public void removeUnknown(List chargeBoxIdList) { + unknownChargePointService.removeAll(chargeBoxIdList); + } +} diff --git a/src/main/java/de/rwth/idsg/steve/web/controller/ChargePointsController.java b/src/main/java/de/rwth/idsg/steve/web/controller/ChargePointsController.java index 53e66f299..7fe90180d 100644 --- a/src/main/java/de/rwth/idsg/steve/web/controller/ChargePointsController.java +++ b/src/main/java/de/rwth/idsg/steve/web/controller/ChargePointsController.java @@ -19,7 +19,7 @@ package de.rwth.idsg.steve.web.controller; import de.rwth.idsg.steve.ocpp.OcppProtocol; -import de.rwth.idsg.steve.service.ChargePointHelperService; +import de.rwth.idsg.steve.service.ChargePointRegistrationService; import de.rwth.idsg.steve.service.ChargePointsService; import de.rwth.idsg.steve.utils.ControllerHelper; import de.rwth.idsg.steve.utils.mapper.ChargePointDetailsMapper; @@ -53,7 +53,7 @@ public class ChargePointsController { private final ChargePointsService chargePointsService; - private final ChargePointHelperService chargePointHelperService; + private final ChargePointRegistrationService chargePointRegistrationService; private static final String PARAMS = "params"; @@ -104,7 +104,7 @@ public String getQuery(@ModelAttribute(PARAMS) ChargePointQueryForm params, Mode private void initList(Model model, ChargePointQueryForm params) { model.addAttribute(PARAMS, params); model.addAttribute("cpList", chargePointsService.getOverview(params)); - model.addAttribute("unknownList", chargePointHelperService.getUnknownChargePoints()); + model.addAttribute("unknownList", chargePointRegistrationService.getUnknownChargePoints()); } @GetMapping(value = DETAILS_PATH) @@ -195,7 +195,7 @@ public String addUnknownChargeBoxId(@PathVariable("chargeBoxId") String chargeBo @PostMapping(value = UNKNOWN_REMOVE_PATH) public String removeUnknownChargeBoxId(@PathVariable("chargeBoxId") String chargeBoxId) { - chargePointHelperService.removeUnknown(Collections.singletonList(chargeBoxId)); + chargePointRegistrationService.removeUnknown(Collections.singletonList(chargeBoxId)); return toOverview(); } @@ -234,11 +234,11 @@ private static void setCommonAttributesForSingleAdd(Model model) { private void add(ChargePointForm form) { chargePointsService.addChargePoint(form); - chargePointHelperService.removeUnknown(Collections.singletonList(form.getChargeBoxId())); + chargePointRegistrationService.removeUnknown(Collections.singletonList(form.getChargeBoxId())); } private void add(List idList) { chargePointsService.addChargePointList(idList); - chargePointHelperService.removeUnknown(idList); + chargePointRegistrationService.removeUnknown(idList); } } From 73bb84f49789e19cf01bbe49863aca2f3380a897 Mon Sep 17 00:00:00 2001 From: Julien Herr Date: Tue, 19 Aug 2025 22:20:48 +0200 Subject: [PATCH 4/4] fix: update after Copilot review --- .../java/de/rwth/idsg/steve/service/UsersService.java | 6 +++--- .../java/de/rwth/idsg/steve/service/WebUsersService.java | 8 ++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/de/rwth/idsg/steve/service/UsersService.java b/src/main/java/de/rwth/idsg/steve/service/UsersService.java index 544dd027b..6d736f185 100644 --- a/src/main/java/de/rwth/idsg/steve/service/UsersService.java +++ b/src/main/java/de/rwth/idsg/steve/service/UsersService.java @@ -43,9 +43,6 @@ public User.Details getDetails(int userPk) { } public User.Details add(UserForm form) { - if (form.getUserPk() == null) { - throw new BadRequestException("userPk must not be null"); - } var id = userRepository.add(form); return userRepository.getDetails(id).orElseThrow( () -> new SteveException("User not found after creation, this should never happen") @@ -53,6 +50,9 @@ public User.Details add(UserForm form) { } public User.Details update(UserForm form) { + if (form.getUserPk() == null) { + throw new BadRequestException("userPk must not be null"); + } userRepository.update(form); return userRepository.getDetails(form.getUserPk()).orElseThrow( () -> new SteveException("User not found after update, this should never happen") diff --git a/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java b/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java index 77934e587..71721bc0e 100644 --- a/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java +++ b/src/main/java/de/rwth/idsg/steve/service/WebUsersService.java @@ -309,8 +309,12 @@ private UserDetails toUserDetailsBaseForm(WebUserBaseForm form) { } private UserDetails toUserDetails(WebUserForm form) { - Assert.hasText(form.getPassword(), "Password may not be empty"); - String encPw = encoder.encode(form.getPassword()); + var rawPassword = form.getPassword(); + var encPw = ""; + if (rawPassword != null) { + Assert.hasText(rawPassword, "Password may not be empty"); + encPw = encoder.encode(rawPassword); + } return User .withUsername(form.getWebUsername()) .password(encPw)