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 ec3a740..a31ff14 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 0000000..550fdb2 --- /dev/null +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoClient.kt @@ -0,0 +1,129 @@ +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.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 + * + * @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) + ) + .also { + if (it.status != HttpStatus.OK) throw HttpException(it.status, "status should be ${HttpStatus.OK}") + } + .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) + ) + .also { + if (it.status != HttpStatus.OK) throw HttpException(it.status, "status should be ${HttpStatus.OK}") + } + .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) + ) + .also { + if (it.status != HttpStatus.OK) throw HttpException(it.status, "status should be ${HttpStatus.OK}") + } + .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) + ) + .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/ChargingProfilesCpoInterface.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesCpoInterface.kt new file mode 100644 index 0000000..2a0b361 --- /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 0000000..4f51cf1 --- /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.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") + ), + queryParams = listOf("duration", "response_url") + ) { req -> + req.httpResponse { + service + .getActiveChargingProfile( + sessionId = req.pathParams["sessionId"]!!, + duration = req.queryParams["duration"]!!.toInt(), + responseUrl = req.queryParams["response_url"]!! + ) + } + } + + 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 0000000..a0c8015 --- /dev/null +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspClient.kt @@ -0,0 +1,112 @@ +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.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, + 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) + ) + .also { + if (it.status != HttpStatus.OK) throw HttpException(it.status, "status should be ${HttpStatus.OK}") + } + .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) + ) + .also { + if (it.status != HttpStatus.OK && it.status != HttpStatus.CREATED) { + throw HttpException(it.status, "status should be ${HttpStatus.OK} or ${HttpStatus.CREATED}") + } + } + .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) + ) + .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/ChargingProfilesScspInterface.kt b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/ChargingProfilesScspInterface.kt new file mode 100644 index 0000000..2b3fad4 --- /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 0000000..3053ee1 --- /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.pathParams["requestId"].orEmpty(), + result = mapper.readValue(req.body, ActiveChargingProfileResult::class.java) + ) + } + } + + transportServer.handle( + method = HttpMethod.POST, + path = basePathSegments + listOf( + FixedPathSegment(CHARGING_PROFILE_CALLBACK_URL), + VariablePathSegment("requestId") + ) + ) { req -> + req.httpResponse { + service + .postCallbackChargingProfile( + requestId = req.pathParams["requestId"].orEmpty(), + result = mapper.readValue(req.body, ChargingProfileResult::class.java) + ) + } + } + + transportServer.handle( + method = HttpMethod.POST, + path = basePathSegments + listOf( + FixedPathSegment(CLEAR_PROFILE_CALLBACK_URL), + VariablePathSegment("requestId") + ) + ) { req -> + req.httpResponse { + service + .postCallbackClearProfile( + requestId = req.pathParams["requestId"].orEmpty(), + 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.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/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 0000000..0c98471 --- /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 0000000..43cd26a --- /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 0000000..0f17c87 --- /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 0000000..567b32c --- /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 0000000..18eccc5 --- /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 0000000..b82fddf --- /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 0000000..02d82c6 --- /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 0000000..6e41b64 --- /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 0000000..3abd7de --- /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 0000000..80bf23c --- /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 0000000..da69f35 --- /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 0000000..bf1d0d3 --- /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 0000000..f1b2589 --- /dev/null +++ b/ocpi-toolkit-2.2.1/src/main/kotlin/com/izivia/ocpi/toolkit/modules/chargingProfiles/services/ChargingProfilesChargePointService.kt @@ -0,0 +1,23 @@ +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 0000000..e7de70c --- /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 0000000..5e9d357 --- /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 0000000..d6624d0 --- /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) +} 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 df5ec72..ade6862 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 b97452f..9bcb071 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,