From 3ef1e5fa549af4cf0c475e95c1947181f63dbf9b Mon Sep 17 00:00:00 2001 From: andacata <1506402+andacata@users.noreply.github.com> Date: Wed, 28 Feb 2024 12:00:14 +0100 Subject: [PATCH 1/6] feat: Add ChargingProfiles module --- .../com/izivia/ocpi/toolkit/common/Utils.kt | 1 + .../ChargingProfilesCpoClient.kt | 115 ++++++++++++++++++ .../ChargingProfilesCpoInterface.kt | 69 +++++++++++ .../ChargingProfilesCpoServer.kt | 76 ++++++++++++ .../ChargingProfilesScspClient.kt | 96 +++++++++++++++ .../ChargingProfilesScspInterface.kt | 48 ++++++++ .../ChargingProfilesScspServer.kt | 103 ++++++++++++++++ .../domain/ActiveChargingProfile.kt | 8 ++ .../domain/ActiveChargingProfileResult.kt | 6 + .../domain/ChargingProfile.kt | 12 ++ .../domain/ChargingProfilePeriod.kt | 8 ++ .../domain/ChargingProfileResponse.kt | 6 + .../domain/ChargingProfileResponseType.kt | 29 +++++ .../domain/ChargingProfileResult.kt | 5 + .../domain/ChargingProfileResultType.kt | 18 +++ .../domain/ChargingRateUnit.kt | 22 ++++ .../domain/ClearProfileResult.kt | 5 + .../domain/SetChargingProfile.kt | 8 ++ .../ChargingProfilesScspRepository.kt | 16 +++ .../ChargingProfilesChargePointService.kt | 25 ++++ .../services/ChargingProfilesCpoService.kt | 48 ++++++++ .../services/ChargingProfilesScspService.kt | 63 ++++++++++ .../services/ChargingProfilesValidators.kt | 43 +++++++ 23 files changed, 830 insertions(+) create mode 100644 ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoClient.kt create mode 100644 ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoInterface.kt create mode 100644 ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoServer.kt create mode 100644 ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspClient.kt create mode 100644 ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspInterface.kt create mode 100644 ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspServer.kt create mode 100644 ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ActiveChargingProfile.kt create mode 100644 ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ActiveChargingProfileResult.kt create mode 100644 ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingProfile.kt create mode 100644 ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingProfilePeriod.kt create mode 100644 ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingProfileResponse.kt create mode 100644 ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingProfileResponseType.kt create mode 100644 ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingProfileResult.kt create mode 100644 ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingProfileResultType.kt create mode 100644 ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingRateUnit.kt create mode 100644 ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ClearProfileResult.kt create mode 100644 ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/SetChargingProfile.kt create mode 100644 ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/repositories/ChargingProfilesScspRepository.kt create mode 100644 ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/services/ChargingProfilesChargePointService.kt create mode 100644 ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/services/ChargingProfilesCpoService.kt create mode 100644 ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/services/ChargingProfilesScspService.kt create mode 100644 ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/services/ChargingProfilesValidators.kt diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/common/Utils.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/common/Utils.kt index ec3a740c..a31ff14a 100644 --- a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/common/Utils.kt +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/common/Utils.kt @@ -7,3 +7,4 @@ fun generateUUIDv4Token(): String { } typealias CiString = String +typealias URL = String diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoClient.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoClient.kt new file mode 100644 index 00000000..7df6f6e2 --- /dev/null +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoClient.kt @@ -0,0 +1,115 @@ +package com.izivia.ocpi.toolkit.modules.chargingProfiles + +import com.izivia.ocpi.toolkit.common.* +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ActiveChargingProfile +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ActiveChargingProfileResult +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ChargingProfileResult +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ClearProfileResult +import com.izivia.ocpi.toolkit.modules.credentials.repositories.PartnerRepository +import com.izivia.ocpi.toolkit.modules.versions.domain.ModuleID +import com.izivia.ocpi.toolkit.transport.TransportClient +import com.izivia.ocpi.toolkit.transport.TransportClientBuilder +import com.izivia.ocpi.toolkit.transport.domain.HttpMethod +import com.izivia.ocpi.toolkit.transport.domain.HttpRequest + +/** + * Send calls to the SCSP + * + * @property transportClientBuilder used to build transport client + * @property serverVersionsEndpointUrl used to know which partner to communicate with + * @property partnerRepository used to get information about the partner (endpoint, token) + * @property callbackBaseUrl used to build the callback URL sent to the other partner + */ +class ChargingProfilesCpoClient( + private val transportClientBuilder: TransportClientBuilder, + private val serverVersionsEndpointUrl: String, + private val partnerRepository: PartnerRepository, + private val callbackBaseUrl: String +) { + + private fun buildCallbackTransport(): TransportClient = + transportClientBuilder.build(callbackBaseUrl) + + private suspend fun buildTransport(): TransportClient = transportClientBuilder + .buildFor( + module = ModuleID.chargingprofiles, + partnerUrl = serverVersionsEndpointUrl, + partnerRepository = partnerRepository + ) + + suspend fun postCallbackActiveChargingProfile( + responseUrl: String, + result: ActiveChargingProfileResult + ): OcpiResponseBody = with(buildCallbackTransport()) { + send( + HttpRequest( + method = HttpMethod.POST, + path = responseUrl, + body = mapper.writeValueAsString(result) + ) + .withRequiredHeaders( + requestId = generateRequestId(), + correlationId = generateCorrelationId() + ) + .authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl) + ) + .parseBody() + } + + suspend fun postCallbackChargingProfile( + responseUrl: String, + result: ChargingProfileResult + ): OcpiResponseBody = with(buildCallbackTransport()) { + send( + HttpRequest( + method = HttpMethod.POST, + path = responseUrl, + body = mapper.writeValueAsString(result) + ) + .withRequiredHeaders( + requestId = generateRequestId(), + correlationId = generateCorrelationId() + ) + .authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl) + ) + .parseBody() + } + + suspend fun postCallbackClearProfile( + responseUrl: String, + result: ClearProfileResult + ): OcpiResponseBody = with(buildCallbackTransport()) { + send( + HttpRequest( + method = HttpMethod.POST, + path = responseUrl, + body = mapper.writeValueAsString(result) + ) + .withRequiredHeaders( + requestId = generateRequestId(), + correlationId = generateCorrelationId() + ) + .authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl) + ) + .parseBody() + } + + suspend fun putActiveChargingProfile( + sessionId: CiString, + activeChargingProfile: ActiveChargingProfile + ): OcpiResponseBody = with(buildTransport()) { + send( + HttpRequest( + method = HttpMethod.PUT, + path = "/$sessionId", + body = mapper.writeValueAsString(activeChargingProfile) + ) + .withRequiredHeaders( + requestId = generateRequestId(), + correlationId = generateCorrelationId() + ) + .authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl) + ) + .parseBody() + } +} diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoInterface.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoInterface.kt new file mode 100644 index 00000000..2a0b3610 --- /dev/null +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoInterface.kt @@ -0,0 +1,69 @@ +package com.izivia.ocpi.toolkit.modules.chargingProfiles + +import com.izivia.ocpi.toolkit.common.CiString +import com.izivia.ocpi.toolkit.common.OcpiResponseBody +import com.izivia.ocpi.toolkit.common.URL +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ChargingProfileResponse +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.SetChargingProfile + +/** + * The ChargingProfiles module consists of two interfaces: a Receiver interface that enables a Sender (and its clients) + * to send ChargingProfiles to a Location/EVSE, and an Sender interface to receive the response from the Location/EVSE + * asynchronously. + * + * This is the receiver interface + * + * Method: Description + * - GET: Gets the ActiveChargingProfile for a specific charging session. + * - POST: n/a (use PUT) + * - PUT: Creates/updates a ChargingProfile for a specific charging session. + * - PATCH: n/a (use PUT) + * - DELETE: Cancels an existing ChargingProfile for a specific charging session. + */ +interface ChargingProfilesCpoInterface { + + /** + * Retrieves the ActiveChargingProfile as it is currently planned for the the given session. + * + * Endpoint structure definition: + * - /ocpi/cpo/2.2.1/chargingprofiles/{session_id}?duration={duration}&response_url={url} + * + * @param sessionId (max-length=36) The unique id that identifies the session in the CPO platform. + * @param duration Length of the requested ActiveChargingProfile in seconds Duration in seconds. + * @param responseUrl (max-length=255) URL that the ActiveChargingProfileResult POST should be send to. This URL + * might contain an unique ID to be able to distinguish between GET ActiveChargingProfile requests. + */ + suspend fun getActiveChargingProfile( + sessionId: CiString, + duration: Int, + responseUrl: URL + ): OcpiResponseBody + + /** + * Creates a new ChargingProfile on a session, or replaces an existing ChargingProfile on the EVSE. + * + * Endpoint structure definition: + * - /ocpi/cpo/2.2.1/chargingprofiles/{session_id} + * + * @param sessionId (max-length=36) The unique id that identifies the session in the CPO platform. + */ + suspend fun putChargingProfile( + sessionId: CiString, + setChargingProfile: SetChargingProfile + ): OcpiResponseBody + + /** + * Clears the ChargingProfile set by the eMSP on the given session. + * + * Endpoint structure definition: + * - /ocpi/cpo/2.2.1/chargingprofiles/{session_id}?response_url={url} + * + * @param sessionId (max-length=36) The unique id that identifies the session in the CPO platform. + * @param responseUrl (max-length=255) URL that the ClearProfileResult POST should be send to. This URL might + * contain an unique ID to be able to distinguish between DELETE ChargingProfile requests. + */ + suspend fun deleteChargingProfile( + sessionId: CiString, + responseUrl: URL + ): OcpiResponseBody +} diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoServer.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoServer.kt new file mode 100644 index 00000000..e4a74635 --- /dev/null +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoServer.kt @@ -0,0 +1,76 @@ +package com.izivia.ocpi.toolkit.modules.chargingProfiles + +import com.izivia.ocpi.toolkit.common.OcpiSelfRegisteringModuleServer +import com.izivia.ocpi.toolkit.common.httpResponse +import com.izivia.ocpi.toolkit.common.mapper +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ChargingProfile +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.SetChargingProfile +import com.izivia.ocpi.toolkit.modules.versions.domain.InterfaceRole +import com.izivia.ocpi.toolkit.modules.versions.domain.ModuleID +import com.izivia.ocpi.toolkit.modules.versions.domain.VersionNumber +import com.izivia.ocpi.toolkit.modules.versions.repositories.MutableVersionsRepository +import com.izivia.ocpi.toolkit.transport.TransportServer +import com.izivia.ocpi.toolkit.transport.domain.HttpMethod +import com.izivia.ocpi.toolkit.transport.domain.VariablePathSegment + +class ChargingProfilesCpoServer( + private val service: ChargingProfilesCpoInterface, + versionsRepository: MutableVersionsRepository? = null, + basePathOverride: String? = null +) : OcpiSelfRegisteringModuleServer( + ocpiVersion = VersionNumber.V2_2_1, + moduleID = ModuleID.chargingprofiles, + interfaceRole = InterfaceRole.RECEIVER, + versionsRepository = versionsRepository, + basePathOverride = basePathOverride +) { + + override suspend fun doRegisterOn(transportServer: TransportServer) { + transportServer.handle( + method = HttpMethod.GET, + path = basePathSegments + listOf( + VariablePathSegment("sessionId") + ) + ) { req -> + req.httpResponse { + service + .getActiveChargingProfile( + sessionId = req.pathParams["sessionId"]!!, + duration = req.queryParams["duration"]!!.toInt(), + responseUrl = req.queryParams["responseUrl"]!! + ) + } + } + + transportServer.handle( + method = HttpMethod.PUT, + path = basePathSegments + listOf( + VariablePathSegment("sessionId") + ) + ) { req -> + req.httpResponse { + service + .putChargingProfile( + sessionId = req.pathParams["sessionId"]!!, + setChargingProfile = mapper.readValue(req.body, SetChargingProfile::class.java) + ) + } + } + + transportServer.handle( + method = HttpMethod.DELETE, + path = basePathSegments + listOf( + VariablePathSegment("sessionId") + ), + queryParams = listOf("response_url") + ) { req -> + req.httpResponse { + service + .deleteChargingProfile( + sessionId = req.pathParams["sessionId"]!!, + responseUrl = req.queryParams["response_url"]!! + ) + } + } + } +} diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspClient.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspClient.kt new file mode 100644 index 00000000..097cac77 --- /dev/null +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspClient.kt @@ -0,0 +1,96 @@ +package com.izivia.ocpi.toolkit.modules.chargingProfiles + +import com.izivia.ocpi.toolkit.common.* +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ChargingProfile +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ChargingProfileResponse +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.SetChargingProfile +import com.izivia.ocpi.toolkit.modules.credentials.repositories.PartnerRepository +import com.izivia.ocpi.toolkit.modules.versions.domain.ModuleID +import com.izivia.ocpi.toolkit.transport.TransportClient +import com.izivia.ocpi.toolkit.transport.TransportClientBuilder +import com.izivia.ocpi.toolkit.transport.domain.HttpMethod +import com.izivia.ocpi.toolkit.transport.domain.HttpRequest + +class ChargingProfilesScspClient( + private val transportClientBuilder: TransportClientBuilder, + private val serverVersionsEndpointUrl: String, + private val partnerRepository: PartnerRepository, + private val callbackBaseUrl: URL +) { + + private suspend fun buildTransport(): TransportClient = transportClientBuilder + .buildFor( + module = ModuleID.chargingprofiles, + partnerUrl = serverVersionsEndpointUrl, + partnerRepository = partnerRepository + ) + + suspend fun getActiveChargingProfile( + sessionId: CiString, + duration: Int, + requestId: String + ): OcpiResponseBody = with(buildTransport()) { + send( + HttpRequest( + method = HttpMethod.GET, + path = "/$sessionId", + queryParams = mapOf( + "duration" to duration.toString(), + "response_url" to "$callbackBaseUrl/${ChargingProfilesScspServer.ACTIVE_CHARGING_PROFILE_CALLBACK_URL}/$requestId" + ) + ) + .withRequiredHeaders( + requestId = generateRequestId(), + correlationId = generateCorrelationId() + ) + .authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl) + ) + .parseBody() + } + + suspend fun putChargingProfile( + sessionId: CiString, + chargingProfile: ChargingProfile, + requestId: String + ): OcpiResponseBody = with(buildTransport()) { + send( + HttpRequest( + method = HttpMethod.PUT, + path = "/$sessionId", + body = mapper.writeValueAsString( + SetChargingProfile( + chargingProfile = chargingProfile, + responseUrl = "$callbackBaseUrl/${ChargingProfilesScspServer.CHARGING_PROFILE_CALLBACK_URL}/$requestId" + ) + ) + ) + .withRequiredHeaders( + requestId = generateRequestId(), + correlationId = generateCorrelationId() + ) + .authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl) + ) + .parseBody() + } + + suspend fun deleteChargingProfile( + sessionId: CiString, + requestId: String + ): OcpiResponseBody = with(buildTransport()) { + send( + HttpRequest( + method = HttpMethod.DELETE, + path = "/$sessionId", + queryParams = mapOf( + "response_url" to "$callbackBaseUrl/${ChargingProfilesScspServer.CLEAR_PROFILE_CALLBACK_URL}/$requestId" + ) + ) + .withRequiredHeaders( + requestId = generateRequestId(), + correlationId = generateCorrelationId() + ) + .authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl) + ) + .parseBody() + } +} diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspInterface.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspInterface.kt new file mode 100644 index 00000000..2b3fad43 --- /dev/null +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspInterface.kt @@ -0,0 +1,48 @@ +package com.izivia.ocpi.toolkit.modules.chargingProfiles + +import com.izivia.ocpi.toolkit.common.CiString +import com.izivia.ocpi.toolkit.common.OcpiResponseBody +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ActiveChargingProfile +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ActiveChargingProfileResult +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ChargingProfileResult +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ClearProfileResult + +/** + * The ChargingProfiles module consists of two interfaces: a Receiver interface that enables a Sender (and its clients) + * to send ChargingProfiles to a Location/EVSE, and an Sender interface to receive the response from the Location/EVSE + * asynchronously. + * + * This is the sender interface + * + * Method: Description + * - GET: n/a + * - POST: Receive the asynchronous response from the Charge Point. + * - PUT: Receiver (typically CPO) can send an updated ActiveChargingProfile when other inputs have made changes to + * existing profile. When the Receiver (typically CPO) sends a update profile to the EVSE, for an other reason + * then the Sender (Typically SCSP) asking, the Sender SHALL post an update to this interface. When a local + * input influence the ActiveChargingProfile in the EVSE AND the Receiver (typically CPO) is made aware of this, + * the Receiver SHALL post an update to this interface. + * - PATCH: n/a + * - DELETE: n/a + */ +interface ChargingProfilesScspInterface { + suspend fun postCallbackActiveChargingProfile( + requestId: String, + result: ActiveChargingProfileResult + ): OcpiResponseBody + + suspend fun postCallbackChargingProfile( + requestId: String, + result: ChargingProfileResult + ): OcpiResponseBody + + suspend fun postCallbackClearProfile( + requestId: String, + result: ClearProfileResult + ): OcpiResponseBody + + suspend fun putActiveChargingProfile( + sessionId: CiString, + activeChargingProfile: ActiveChargingProfile + ): OcpiResponseBody +} diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspServer.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspServer.kt new file mode 100644 index 00000000..1e8bf0e9 --- /dev/null +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspServer.kt @@ -0,0 +1,103 @@ +package com.izivia.ocpi.toolkit.modules.chargingProfiles + +import com.izivia.ocpi.toolkit.common.OcpiSelfRegisteringModuleServer +import com.izivia.ocpi.toolkit.common.httpResponse +import com.izivia.ocpi.toolkit.common.mapper +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ActiveChargingProfile +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ActiveChargingProfileResult +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ChargingProfileResult +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ClearProfileResult +import com.izivia.ocpi.toolkit.modules.versions.domain.InterfaceRole +import com.izivia.ocpi.toolkit.modules.versions.domain.ModuleID +import com.izivia.ocpi.toolkit.modules.versions.domain.VersionNumber +import com.izivia.ocpi.toolkit.modules.versions.repositories.MutableVersionsRepository +import com.izivia.ocpi.toolkit.transport.TransportServer +import com.izivia.ocpi.toolkit.transport.domain.FixedPathSegment +import com.izivia.ocpi.toolkit.transport.domain.HttpMethod +import com.izivia.ocpi.toolkit.transport.domain.VariablePathSegment + +class ChargingProfilesScspServer( + private val service: ChargingProfilesScspInterface, + versionsRepository: MutableVersionsRepository? = null, + basePathOverride: String? = null +) : OcpiSelfRegisteringModuleServer( + ocpiVersion = VersionNumber.V2_2_1, + moduleID = ModuleID.chargingprofiles, + interfaceRole = InterfaceRole.SENDER, + versionsRepository = versionsRepository, + basePathOverride = basePathOverride +) { + + companion object { + const val ACTIVE_CHARGING_PROFILE_CALLBACK_URL = "activechargingprofile/callback" + const val CHARGING_PROFILE_CALLBACK_URL = "chargingprofile/callback" + const val CLEAR_PROFILE_CALLBACK_URL = "clearprofile/callback" + const val PUT_ACTIVE_CHARGING_PROFILE_URL = "activechargingprofile" + } + + override suspend fun doRegisterOn(transportServer: TransportServer) { + transportServer.handle( + method = HttpMethod.POST, + path = basePathSegments + listOf( + FixedPathSegment(ACTIVE_CHARGING_PROFILE_CALLBACK_URL), + VariablePathSegment("requestId") + ) + ) { req -> + req.httpResponse { + service + .postCallbackActiveChargingProfile( + requestId = req.queryParams["requestId"] ?: "", + result = mapper.readValue(req.body, ActiveChargingProfileResult::class.java) + ) + } + } + + transportServer.handle( + method = HttpMethod.POST, + path = basePathSegments + listOf( + FixedPathSegment(CHARGING_PROFILE_CALLBACK_URL), + VariablePathSegment("chargingProfileId") + ) + ) { req -> + req.httpResponse { + service + .postCallbackChargingProfile( + requestId = req.queryParams["requestId"] ?: "", + result = mapper.readValue(req.body, ChargingProfileResult::class.java) + ) + } + } + + transportServer.handle( + method = HttpMethod.POST, + path = basePathSegments + listOf( + FixedPathSegment(CLEAR_PROFILE_CALLBACK_URL), + VariablePathSegment("chargingProfileId") + ) + ) { req -> + req.httpResponse { + service + .postCallbackClearProfile( + requestId = req.queryParams["requestId"] ?: "", + result = mapper.readValue(req.body, ClearProfileResult::class.java) + ) + } + } + + transportServer.handle( + method = HttpMethod.PUT, + path = basePathSegments + listOf( + FixedPathSegment(PUT_ACTIVE_CHARGING_PROFILE_URL), + VariablePathSegment("sessionId") + ) + ) { req -> + req.httpResponse { + service + .putActiveChargingProfile( + sessionId = req.queryParams["sessionId"] ?: "", + activeChargingProfile = mapper.readValue(req.body, ActiveChargingProfile::class.java) + ) + } + } + } +} diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ActiveChargingProfile.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ActiveChargingProfile.kt new file mode 100644 index 00000000..0c98471d --- /dev/null +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ActiveChargingProfile.kt @@ -0,0 +1,8 @@ +package com.izivia.ocpi.toolkit.modules.chargingProfiles.domain + +import java.time.Instant + +data class ActiveChargingProfile( + val startDateTime: Instant, + val chargingProfile: ChargingProfile +) diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ActiveChargingProfileResult.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ActiveChargingProfileResult.kt new file mode 100644 index 00000000..43cd26ab --- /dev/null +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ActiveChargingProfileResult.kt @@ -0,0 +1,6 @@ +package com.izivia.ocpi.toolkit.modules.chargingProfiles.domain + +data class ActiveChargingProfileResult( + val result: ChargingProfileResultType, + val profile: ActiveChargingProfile? = null +) diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingProfile.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingProfile.kt new file mode 100644 index 00000000..0f17c87a --- /dev/null +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingProfile.kt @@ -0,0 +1,12 @@ +package com.izivia.ocpi.toolkit.modules.chargingProfiles.domain + +import java.math.BigDecimal +import java.time.Instant + +data class ChargingProfile( + val startDateTime: Instant? = null, + val duration: Int? = null, + val chargingRateUnit: ChargingRateUnit, + val minChargingRate: BigDecimal?, + val chargingProfilePeriod: List? +) diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingProfilePeriod.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingProfilePeriod.kt new file mode 100644 index 00000000..567b32ca --- /dev/null +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingProfilePeriod.kt @@ -0,0 +1,8 @@ +package com.izivia.ocpi.toolkit.modules.chargingProfiles.domain + +import java.math.BigDecimal + +data class ChargingProfilePeriod( + val startPeriod: Int, + val limit: BigDecimal +) diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingProfileResponse.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingProfileResponse.kt new file mode 100644 index 00000000..18eccc5d --- /dev/null +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingProfileResponse.kt @@ -0,0 +1,6 @@ +package com.izivia.ocpi.toolkit.modules.chargingProfiles.domain + +data class ChargingProfileResponse( + val result: ChargingProfileResponseType, + val timeout: Int +) diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingProfileResponseType.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingProfileResponseType.kt new file mode 100644 index 00000000..b82fddf3 --- /dev/null +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingProfileResponseType.kt @@ -0,0 +1,29 @@ +package com.izivia.ocpi.toolkit.modules.chargingProfiles.domain + +enum class ChargingProfileResponseType { + /** + * ChargingProfile request accepted by the CPO, request will be forwarded to the EVSE. + */ + ACCEPTED, + + /** + * The ChargingProfiles not supported by this CPO, Charge Point, EVSE etc. + */ + NOT_SUPPORTED, + + /** + * ChargingProfile request rejected by the CPO. (Session might not be from a customer of the eMSP + * that send this request) + */ + REJECTED, + + /** + * ChargingProfile request rejected by the CPO, requests are send more often then allowed. + */ + TOO_OFTEN, + + /** + * The Session in the requested command is not known by this CPO. + */ + UNKNOWN_SESSION +} diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingProfileResult.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingProfileResult.kt new file mode 100644 index 00000000..02d82c66 --- /dev/null +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingProfileResult.kt @@ -0,0 +1,5 @@ +package com.izivia.ocpi.toolkit.modules.chargingProfiles.domain + +data class ChargingProfileResult( + val result: ChargingProfileResultType +) diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingProfileResultType.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingProfileResultType.kt new file mode 100644 index 00000000..6e41b646 --- /dev/null +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingProfileResultType.kt @@ -0,0 +1,18 @@ +package com.izivia.ocpi.toolkit.modules.chargingProfiles.domain + +enum class ChargingProfileResultType { + /** + * ChargingProfile request accepted by the EVSE. + */ + ACCEPTED, + + /** + * ChargingProfile request rejected by the EVSE. + */ + REJECTED, + + /** + * No Charging Profile(s) were found by the EVSE matching the request. + */ + UNKNOWN +} diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingRateUnit.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingRateUnit.kt new file mode 100644 index 00000000..3abd7def --- /dev/null +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ChargingRateUnit.kt @@ -0,0 +1,22 @@ +package com.izivia.ocpi.toolkit.modules.chargingProfiles.domain + +enum class ChargingRateUnit { + /** + * Watts (power) + * This is the TOTAL allowed charging power. If used for AC Charging, the phase current should be + * calculated via: Current per phase = Power / (Line Voltage * Number of Phases). The "Line Voltage" + * used in the calculation is the Line to Neutral Voltage (VLN). In Europe and Asia VLN is typically + * 220V or 230V and the corresponding Line to Line Voltage (VLL) is 380V and 400V. The "Number of + * Phases" is the numberPhases from the ChargingProfilePeriod. It is usually more convenient to use + * this for DC charging. Note that if numberPhases in a ChargingProfilePeriod is absent, 3 SHALL be + * assumed. + */ + W, + + /** + * Amperes (current) + * The amount of Ampere per phase, not the sum of all phases. It is usually more convenient to use + * this for AC charging. + */ + A +} diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ClearProfileResult.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ClearProfileResult.kt new file mode 100644 index 00000000..80bf23c6 --- /dev/null +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/ClearProfileResult.kt @@ -0,0 +1,5 @@ +package com.izivia.ocpi.toolkit.modules.chargingProfiles.domain + +data class ClearProfileResult( + val result: ChargingProfileResultType +) diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/SetChargingProfile.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/SetChargingProfile.kt new file mode 100644 index 00000000..da69f35a --- /dev/null +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/domain/SetChargingProfile.kt @@ -0,0 +1,8 @@ +package com.izivia.ocpi.toolkit.modules.chargingProfiles.domain + +import com.izivia.ocpi.toolkit.common.URL + +data class SetChargingProfile( + val chargingProfile: ChargingProfile, + val responseUrl: URL +) diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/repositories/ChargingProfilesScspRepository.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/repositories/ChargingProfilesScspRepository.kt new file mode 100644 index 00000000..bf1d0d3a --- /dev/null +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/repositories/ChargingProfilesScspRepository.kt @@ -0,0 +1,16 @@ +package com.izivia.ocpi.toolkit.modules.chargingProfiles.repositories + +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ActiveChargingProfile +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ActiveChargingProfileResult +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ChargingProfileResult +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ClearProfileResult + +interface ChargingProfilesScspRepository { + suspend fun postCallbackActiveChargingProfile(requestId: String, result: ActiveChargingProfileResult) + + suspend fun postCallbackChargingProfile(requestId: String, result: ChargingProfileResult) + + suspend fun postCallbackClearProfile(requestId: String, result: ClearProfileResult) + + suspend fun putActiveChargingProfile(sessionId: String, activeChargingProfile: ActiveChargingProfile) +} diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/services/ChargingProfilesChargePointService.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/services/ChargingProfilesChargePointService.kt new file mode 100644 index 00000000..b914efe9 --- /dev/null +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/services/ChargingProfilesChargePointService.kt @@ -0,0 +1,25 @@ +package com.izivia.ocpi.toolkit.modules.chargingProfiles.services + +import com.izivia.ocpi.toolkit.common.URL +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ChargingProfileResponse +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.SetChargingProfile + +interface ChargingProfilesChargePointService { + suspend fun getActiveChargingProfile( + sessionId: String, + duration: Int, + responseUrl: URL + ): ChargingProfileResponse + + suspend fun putChargingProfile( + sessionId: String, + setChargingProfile: SetChargingProfile + ): ChargingProfileResponse + + suspend fun deleteChargingProfile( + sessionId: String, + responseUrl: URL + ): ChargingProfileResponse + + +} diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/services/ChargingProfilesCpoService.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/services/ChargingProfilesCpoService.kt new file mode 100644 index 00000000..e7de70c9 --- /dev/null +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/services/ChargingProfilesCpoService.kt @@ -0,0 +1,48 @@ +package com.izivia.ocpi.toolkit.modules.chargingProfiles.services + +import com.izivia.ocpi.toolkit.common.CiString +import com.izivia.ocpi.toolkit.common.OcpiResponseBody +import com.izivia.ocpi.toolkit.common.URL +import com.izivia.ocpi.toolkit.common.validation.validate +import com.izivia.ocpi.toolkit.common.validation.validateInt +import com.izivia.ocpi.toolkit.common.validation.validateLength +import com.izivia.ocpi.toolkit.modules.chargingProfiles.ChargingProfilesCpoInterface +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ChargingProfileResponse +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.SetChargingProfile + +open class ChargingProfilesCpoService( + private val service: ChargingProfilesChargePointService +) : ChargingProfilesCpoInterface { + + override suspend fun getActiveChargingProfile( + sessionId: CiString, + duration: Int, + responseUrl: URL + ): OcpiResponseBody = + validate { + validateLength("sessionId", sessionId, 36) + validateInt("duration", duration, 0, null) + validateLength("response_url", responseUrl, 255) + } + .let { OcpiResponseBody.success(service.getActiveChargingProfile(sessionId, duration, responseUrl)) } + + override suspend fun putChargingProfile( + sessionId: CiString, + setChargingProfile: SetChargingProfile + ): OcpiResponseBody = + validate { + validateLength("sessionId", sessionId, 36) + setChargingProfile.validate() + } + .let { OcpiResponseBody.success(service.putChargingProfile(sessionId, setChargingProfile)) } + + override suspend fun deleteChargingProfile( + sessionId: CiString, + responseUrl: URL + ): OcpiResponseBody = + validate { + validateLength("sessionId", sessionId, 36) + validateLength("response_url", responseUrl, 255) + } + .let { OcpiResponseBody.success(service.deleteChargingProfile(sessionId, responseUrl)) } +} diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/services/ChargingProfilesScspService.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/services/ChargingProfilesScspService.kt new file mode 100644 index 00000000..5e9d3579 --- /dev/null +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/services/ChargingProfilesScspService.kt @@ -0,0 +1,63 @@ +package com.izivia.ocpi.toolkit.modules.chargingProfiles.services + +import com.izivia.ocpi.toolkit.common.CiString +import com.izivia.ocpi.toolkit.common.OcpiResponseBody +import com.izivia.ocpi.toolkit.common.validation.validate +import com.izivia.ocpi.toolkit.modules.chargingProfiles.ChargingProfilesScspInterface +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ActiveChargingProfile +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ActiveChargingProfileResult +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ChargingProfileResult +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ClearProfileResult +import com.izivia.ocpi.toolkit.modules.chargingProfiles.repositories.ChargingProfilesScspRepository + +open class ChargingProfilesScspService( + private val repository: ChargingProfilesScspRepository +) : ChargingProfilesScspInterface { + override suspend fun postCallbackActiveChargingProfile( + requestId: String, + result: ActiveChargingProfileResult + ): OcpiResponseBody = OcpiResponseBody.of { + validate { + result.validate() + } + + repository + .postCallbackActiveChargingProfile(requestId, result) + } + + override suspend fun postCallbackChargingProfile( + requestId: String, + result: ChargingProfileResult + ): OcpiResponseBody = OcpiResponseBody.of { + validate { + result.validate() + } + + repository + .postCallbackChargingProfile(requestId, result) + } + + override suspend fun postCallbackClearProfile( + requestId: String, + result: ClearProfileResult + ): OcpiResponseBody = OcpiResponseBody.of { + validate { + result.validate() + } + + repository + .postCallbackClearProfile(requestId, result) + } + + override suspend fun putActiveChargingProfile( + sessionId: CiString, + activeChargingProfile: ActiveChargingProfile + ): OcpiResponseBody = OcpiResponseBody.of { + validate { + activeChargingProfile.validate() + } + + repository + .putActiveChargingProfile(sessionId, activeChargingProfile) + } +} diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/services/ChargingProfilesValidators.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/services/ChargingProfilesValidators.kt new file mode 100644 index 00000000..d6624d07 --- /dev/null +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/services/ChargingProfilesValidators.kt @@ -0,0 +1,43 @@ +package com.izivia.ocpi.toolkit.modules.chargingProfiles.services + +import com.izivia.ocpi.toolkit.common.validation.hasMaxLengthOf +import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.* +import org.valiktor.functions.isGreaterThan +import org.valiktor.functions.isGreaterThanOrEqualTo +import org.valiktor.validate + +fun ActiveChargingProfileResult.validate(): ActiveChargingProfileResult = validate(this) { + // result: nothing to validate + profile?.validate() +} + +fun ActiveChargingProfile.validate(): ActiveChargingProfile = validate(this) { + // startDateTime: nothing to validate + chargingProfile.validate() +} + +fun ChargingProfile.validate(): ChargingProfile = validate(this) { + // startDateTime: nothing to validate + duration?.also { validate(ChargingProfile::duration).isGreaterThan(0) } + // chargingRateUnit: nothing to validate + minChargingRate?.also { validate(ChargingProfile::minChargingRate).isGreaterThanOrEqualTo(0.toBigDecimal()) } + chargingProfilePeriod?.forEach { it.validate() } +} + +fun ChargingProfilePeriod.validate(): ChargingProfilePeriod = validate(this) { + validate(ChargingProfilePeriod::startPeriod).isGreaterThanOrEqualTo(0) + validate(ChargingProfilePeriod::limit).isGreaterThanOrEqualTo(0.toBigDecimal()) +} + +fun ChargingProfileResult.validate(): ChargingProfileResult = validate(this) { + // result: nothing to validate +} + +fun ClearProfileResult.validate(): ClearProfileResult = validate(this) { + // result: nothing to validate +} + +fun SetChargingProfile.validate(): SetChargingProfile = validate(this) { + chargingProfile.validate() + validate(SetChargingProfile::responseUrl).hasMaxLengthOf(255) +} From 8c181cc0b4612ab7e51992bd01735610e8fb8399 Mon Sep 17 00:00:00 2001 From: andacata <1506402+andacata@users.noreply.github.com> Date: Wed, 6 Mar 2024 17:24:30 +0100 Subject: [PATCH 2/6] feat: Add ChargingProfiles module --- .../chargingProfiles/ChargingProfilesCpoServer.kt | 1 - .../chargingProfiles/ChargingProfilesScspClient.kt | 14 +++++++++++--- .../services/ChargingProfilesChargePointService.kt | 2 -- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoServer.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoServer.kt index e4a74635..d9bf096c 100644 --- a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoServer.kt +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoServer.kt @@ -3,7 +3,6 @@ package com.izivia.ocpi.toolkit.modules.chargingProfiles import com.izivia.ocpi.toolkit.common.OcpiSelfRegisteringModuleServer import com.izivia.ocpi.toolkit.common.httpResponse import com.izivia.ocpi.toolkit.common.mapper -import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.ChargingProfile import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.SetChargingProfile import com.izivia.ocpi.toolkit.modules.versions.domain.InterfaceRole import com.izivia.ocpi.toolkit.modules.versions.domain.ModuleID diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspClient.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspClient.kt index 097cac77..fe837e3c 100644 --- a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspClient.kt +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspClient.kt @@ -8,8 +8,10 @@ import com.izivia.ocpi.toolkit.modules.credentials.repositories.PartnerRepositor import com.izivia.ocpi.toolkit.modules.versions.domain.ModuleID import com.izivia.ocpi.toolkit.transport.TransportClient import com.izivia.ocpi.toolkit.transport.TransportClientBuilder +import com.izivia.ocpi.toolkit.transport.domain.HttpException import com.izivia.ocpi.toolkit.transport.domain.HttpMethod import com.izivia.ocpi.toolkit.transport.domain.HttpRequest +import com.izivia.ocpi.toolkit.transport.domain.HttpStatus class ChargingProfilesScspClient( private val transportClientBuilder: TransportClientBuilder, @@ -36,7 +38,8 @@ class ChargingProfilesScspClient( path = "/$sessionId", queryParams = mapOf( "duration" to duration.toString(), - "response_url" to "$callbackBaseUrl/${ChargingProfilesScspServer.ACTIVE_CHARGING_PROFILE_CALLBACK_URL}/$requestId" + "response_url" to "$callbackBaseUrl/" + + "${ChargingProfilesScspServer.ACTIVE_CHARGING_PROFILE_CALLBACK_URL}/$requestId" ) ) .withRequiredHeaders( @@ -45,6 +48,7 @@ class ChargingProfilesScspClient( ) .authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl) ) + .also { if (it.status != HttpStatus.OK) throw HttpException(it.status, "status should be ${HttpStatus.OK}") } .parseBody() } @@ -60,7 +64,8 @@ class ChargingProfilesScspClient( body = mapper.writeValueAsString( SetChargingProfile( chargingProfile = chargingProfile, - responseUrl = "$callbackBaseUrl/${ChargingProfilesScspServer.CHARGING_PROFILE_CALLBACK_URL}/$requestId" + responseUrl = "$callbackBaseUrl/" + + "${ChargingProfilesScspServer.CHARGING_PROFILE_CALLBACK_URL}/$requestId" ) ) ) @@ -70,6 +75,7 @@ class ChargingProfilesScspClient( ) .authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl) ) + .also { if (it.status != HttpStatus.OK) throw HttpException(it.status, "status should be ${HttpStatus.OK}") } .parseBody() } @@ -82,7 +88,8 @@ class ChargingProfilesScspClient( method = HttpMethod.DELETE, path = "/$sessionId", queryParams = mapOf( - "response_url" to "$callbackBaseUrl/${ChargingProfilesScspServer.CLEAR_PROFILE_CALLBACK_URL}/$requestId" + "response_url" to "$callbackBaseUrl/" + + "${ChargingProfilesScspServer.CLEAR_PROFILE_CALLBACK_URL}/$requestId" ) ) .withRequiredHeaders( @@ -91,6 +98,7 @@ class ChargingProfilesScspClient( ) .authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl) ) + .also { if (it.status != HttpStatus.OK) throw HttpException(it.status, "status should be ${HttpStatus.OK}") } .parseBody() } } diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/services/ChargingProfilesChargePointService.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/services/ChargingProfilesChargePointService.kt index b914efe9..f1b25894 100644 --- a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/services/ChargingProfilesChargePointService.kt +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/services/ChargingProfilesChargePointService.kt @@ -20,6 +20,4 @@ interface ChargingProfilesChargePointService { sessionId: String, responseUrl: URL ): ChargingProfileResponse - - } From 0206fc7fbb0e52751721bf3863d8257d1439425b Mon Sep 17 00:00:00 2001 From: andacata <1506402+andacata@users.noreply.github.com> Date: Wed, 6 Mar 2024 18:23:24 +0100 Subject: [PATCH 3/6] feat: Add ChargingProfiles module --- .../modules/chargingProfiles/ChargingProfilesCpoClient.kt | 6 ++++++ .../modules/chargingProfiles/ChargingProfilesScspClient.kt | 6 +++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoClient.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoClient.kt index 7df6f6e2..d9ee0b63 100644 --- a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoClient.kt +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoClient.kt @@ -9,8 +9,10 @@ import com.izivia.ocpi.toolkit.modules.credentials.repositories.PartnerRepositor import com.izivia.ocpi.toolkit.modules.versions.domain.ModuleID import com.izivia.ocpi.toolkit.transport.TransportClient import com.izivia.ocpi.toolkit.transport.TransportClientBuilder +import com.izivia.ocpi.toolkit.transport.domain.HttpException import com.izivia.ocpi.toolkit.transport.domain.HttpMethod import com.izivia.ocpi.toolkit.transport.domain.HttpRequest +import com.izivia.ocpi.toolkit.transport.domain.HttpStatus /** * Send calls to the SCSP @@ -53,6 +55,7 @@ class ChargingProfilesCpoClient( ) .authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl) ) + .also { if (it.status != HttpStatus.OK) throw HttpException(it.status, "status should be ${HttpStatus.OK}") } .parseBody() } @@ -72,6 +75,7 @@ class ChargingProfilesCpoClient( ) .authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl) ) + .also { if (it.status != HttpStatus.OK) throw HttpException(it.status, "status should be ${HttpStatus.OK}") } .parseBody() } @@ -91,6 +95,7 @@ class ChargingProfilesCpoClient( ) .authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl) ) + .also { if (it.status != HttpStatus.OK) throw HttpException(it.status, "status should be ${HttpStatus.OK}") } .parseBody() } @@ -110,6 +115,7 @@ class ChargingProfilesCpoClient( ) .authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl) ) + .also { if (it.status != HttpStatus.OK) throw HttpException(it.status, "status should be ${HttpStatus.OK}") } .parseBody() } } diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspClient.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspClient.kt index fe837e3c..77f16be0 100644 --- a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspClient.kt +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspClient.kt @@ -75,7 +75,11 @@ class ChargingProfilesScspClient( ) .authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl) ) - .also { if (it.status != HttpStatus.OK) throw HttpException(it.status, "status should be ${HttpStatus.OK}") } + .also { + if (it.status != HttpStatus.OK && it.status != HttpStatus.CREATED) { + throw HttpException(it.status, "status should be ${HttpStatus.OK} or ${HttpStatus.CREATED}") + } + } .parseBody() } From 7922a282046d0904db76a033d8d541217e6a46fe Mon Sep 17 00:00:00 2001 From: andacata <1506402+andacata@users.noreply.github.com> Date: Wed, 6 Mar 2024 18:35:31 +0100 Subject: [PATCH 4/6] feat: Add ChargingProfiles module --- .../ChargingProfilesCpoClient.kt | 16 ++++++++++++---- .../ChargingProfilesScspClient.kt | 8 ++++++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoClient.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoClient.kt index d9ee0b63..550fdb23 100644 --- a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoClient.kt +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoClient.kt @@ -55,7 +55,9 @@ class ChargingProfilesCpoClient( ) .authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl) ) - .also { if (it.status != HttpStatus.OK) throw HttpException(it.status, "status should be ${HttpStatus.OK}") } + .also { + if (it.status != HttpStatus.OK) throw HttpException(it.status, "status should be ${HttpStatus.OK}") + } .parseBody() } @@ -75,7 +77,9 @@ class ChargingProfilesCpoClient( ) .authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl) ) - .also { if (it.status != HttpStatus.OK) throw HttpException(it.status, "status should be ${HttpStatus.OK}") } + .also { + if (it.status != HttpStatus.OK) throw HttpException(it.status, "status should be ${HttpStatus.OK}") + } .parseBody() } @@ -95,7 +99,9 @@ class ChargingProfilesCpoClient( ) .authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl) ) - .also { if (it.status != HttpStatus.OK) throw HttpException(it.status, "status should be ${HttpStatus.OK}") } + .also { + if (it.status != HttpStatus.OK) throw HttpException(it.status, "status should be ${HttpStatus.OK}") + } .parseBody() } @@ -115,7 +121,9 @@ class ChargingProfilesCpoClient( ) .authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl) ) - .also { if (it.status != HttpStatus.OK) throw HttpException(it.status, "status should be ${HttpStatus.OK}") } + .also { + if (it.status != HttpStatus.OK) throw HttpException(it.status, "status should be ${HttpStatus.OK}") + } .parseBody() } } diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspClient.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspClient.kt index 77f16be0..a0c8015b 100644 --- a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspClient.kt +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspClient.kt @@ -48,7 +48,9 @@ class ChargingProfilesScspClient( ) .authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl) ) - .also { if (it.status != HttpStatus.OK) throw HttpException(it.status, "status should be ${HttpStatus.OK}") } + .also { + if (it.status != HttpStatus.OK) throw HttpException(it.status, "status should be ${HttpStatus.OK}") + } .parseBody() } @@ -102,7 +104,9 @@ class ChargingProfilesScspClient( ) .authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl) ) - .also { if (it.status != HttpStatus.OK) throw HttpException(it.status, "status should be ${HttpStatus.OK}") } + .also { + if (it.status != HttpStatus.OK) throw HttpException(it.status, "status should be ${HttpStatus.OK}") + } .parseBody() } } From bbd5fb58b1cd0ca2978aa917825a8d4bc3ec4a40 Mon Sep 17 00:00:00 2001 From: andacata <1506402+andacata@users.noreply.github.com> Date: Thu, 7 Mar 2024 13:05:59 +0100 Subject: [PATCH 5/6] feat: Add ChargingProfiles module --- .../modules/chargingProfiles/ChargingProfilesScspServer.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspServer.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspServer.kt index 1e8bf0e9..d285b855 100644 --- a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspServer.kt +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspServer.kt @@ -56,7 +56,7 @@ class ChargingProfilesScspServer( method = HttpMethod.POST, path = basePathSegments + listOf( FixedPathSegment(CHARGING_PROFILE_CALLBACK_URL), - VariablePathSegment("chargingProfileId") + VariablePathSegment("requestId") ) ) { req -> req.httpResponse { @@ -72,7 +72,7 @@ class ChargingProfilesScspServer( method = HttpMethod.POST, path = basePathSegments + listOf( FixedPathSegment(CLEAR_PROFILE_CALLBACK_URL), - VariablePathSegment("chargingProfileId") + VariablePathSegment("requestId") ) ) { req -> req.httpResponse { From a8e1200f04f193a8b2612567462f1655871a1aca Mon Sep 17 00:00:00 2001 From: andacata <1506402+andacata@users.noreply.github.com> Date: Mon, 11 Mar 2024 11:18:56 +0100 Subject: [PATCH 6/6] feat: Add ChargingProfiles module --- .../modules/chargingProfiles/ChargingProfilesCpoServer.kt | 5 +++-- .../chargingProfiles/ChargingProfilesScspServer.kt | 8 ++++---- .../ocpi/toolkit/modules/credentials/CredentialsClient.kt | 4 ++-- .../credentials/services/CredentialsClientService.kt | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoServer.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoServer.kt index d9bf096c..4f51cf12 100644 --- a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoServer.kt +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoServer.kt @@ -29,14 +29,15 @@ class ChargingProfilesCpoServer( method = HttpMethod.GET, path = basePathSegments + listOf( VariablePathSegment("sessionId") - ) + ), + queryParams = listOf("duration", "response_url") ) { req -> req.httpResponse { service .getActiveChargingProfile( sessionId = req.pathParams["sessionId"]!!, duration = req.queryParams["duration"]!!.toInt(), - responseUrl = req.queryParams["responseUrl"]!! + responseUrl = req.queryParams["response_url"]!! ) } } diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspServer.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspServer.kt index d285b855..3053ee1f 100644 --- a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspServer.kt +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspServer.kt @@ -46,7 +46,7 @@ class ChargingProfilesScspServer( req.httpResponse { service .postCallbackActiveChargingProfile( - requestId = req.queryParams["requestId"] ?: "", + requestId = req.pathParams["requestId"].orEmpty(), result = mapper.readValue(req.body, ActiveChargingProfileResult::class.java) ) } @@ -62,7 +62,7 @@ class ChargingProfilesScspServer( req.httpResponse { service .postCallbackChargingProfile( - requestId = req.queryParams["requestId"] ?: "", + requestId = req.pathParams["requestId"].orEmpty(), result = mapper.readValue(req.body, ChargingProfileResult::class.java) ) } @@ -78,7 +78,7 @@ class ChargingProfilesScspServer( req.httpResponse { service .postCallbackClearProfile( - requestId = req.queryParams["requestId"] ?: "", + requestId = req.pathParams["requestId"].orEmpty(), result = mapper.readValue(req.body, ClearProfileResult::class.java) ) } @@ -94,7 +94,7 @@ class ChargingProfilesScspServer( req.httpResponse { service .putActiveChargingProfile( - sessionId = req.queryParams["sessionId"] ?: "", + sessionId = req.pathParams["sessionId"].orEmpty(), activeChargingProfile = mapper.readValue(req.body, ActiveChargingProfile::class.java) ) } diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/credentials/CredentialsClient.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/credentials/CredentialsClient.kt index df5ec727..ade68624 100644 --- a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/credentials/CredentialsClient.kt +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/credentials/CredentialsClient.kt @@ -27,7 +27,7 @@ class CredentialsClient( .parseBody() override suspend fun post( - tokenA: String, + token: String, credentials: Credentials, debugHeaders: Map ): OcpiResponseBody = @@ -41,7 +41,7 @@ class CredentialsClient( requestId = transportClient.generateRequestId(), correlationId = transportClient.generateCorrelationId() ) - .authenticate(token = tokenA) + .authenticate(token = token) ) .parseBody() diff --git a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/credentials/services/CredentialsClientService.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/credentials/services/CredentialsClientService.kt index b97452f8..9bcb071f 100644 --- a/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/credentials/services/CredentialsClientService.kt +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/credentials/services/CredentialsClientService.kt @@ -95,7 +95,7 @@ open class CredentialsClientService( // Initiate registration process val credentials = buildCredentialClient().post( - tokenA = credentialsTokenA, + token = credentialsTokenA, credentials = Credentials( token = serverToken, url = clientVersionsEndpointUrl,