Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(commands) #26: Implement Commands module #82

Merged
merged 2 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.izivia.ocpi.toolkit.common

import com.izivia.ocpi.toolkit.transport.domain.HttpRequest

/**
* Example implementation
*
* httpAuth = { req ->
* partnerRepository.getPartnerUrlByCredentialsServerToken(req.parseAuthorizationHeader())!!
* }
*/
fun interface HttpAuthInterface {
suspend fun partnerUrlFromRequest(req: HttpRequest): String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.izivia.ocpi.toolkit.modules.commands

import com.izivia.ocpi.toolkit.common.*
import com.izivia.ocpi.toolkit.modules.commands.domain.CommandResult
import com.izivia.ocpi.toolkit.modules.credentials.repositories.PartnerRepository
import com.izivia.ocpi.toolkit.transport.TransportClientBuilder
import com.izivia.ocpi.toolkit.transport.domain.HttpMethod
import com.izivia.ocpi.toolkit.transport.domain.HttpRequest

class CommandCpoClient(
private val transportClientBuilder: TransportClientBuilder,
private val partnerRepository: PartnerRepository
) {
suspend fun postCommandCallback(
commandResult: CommandResult,
partnerUrl: String,
responseUrl: String
): OcpiResponseBody<Any> =
transportClientBuilder
.build("")
.send(
HttpRequest(
method = HttpMethod.POST,
path = responseUrl,
body = mapper.writeValueAsString(commandResult)
)
.withRequiredHeaders(
requestId = generateUUIDv4Token(),
correlationId = generateUUIDv4Token()
)
.authenticate(partnerRepository = partnerRepository, partnerUrl = partnerUrl)
).parseBody()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.izivia.ocpi.toolkit.modules.commands

import com.izivia.ocpi.toolkit.common.OcpiResponseBody
import com.izivia.ocpi.toolkit.modules.commands.domain.*

interface CommandCpoInterface {
suspend fun postStartSession(
partnerUrl: String,
startSession: StartSession
): OcpiResponseBody<CommandResponse>

suspend fun postStopSession(
partnerUrl: String,
stopSession: StopSession
): OcpiResponseBody<CommandResponse>

suspend fun postReserveNow(
partnerUrl: String,
reserveNow: ReserveNow
): OcpiResponseBody<CommandResponse>

suspend fun postCancelReservation(
partnerUrl: String,
cancelReservation: CancelReservation
): OcpiResponseBody<CommandResponse>

suspend fun postUnlockConnector(
partnerUrl: String,
unlockConnector: UnlockConnector
): OcpiResponseBody<CommandResponse>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.izivia.ocpi.toolkit.modules.commands

import com.izivia.ocpi.toolkit.common.HttpAuthInterface
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.commands.domain.*
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

class CommandCpoServer(
private val httpAuth: HttpAuthInterface,
private val service: CommandCpoInterface,
versionsRepository: MutableVersionsRepository? = null,
basePathOverride: String? = null
) : OcpiSelfRegisteringModuleServer(
ocpiVersion = VersionNumber.V2_2_1,
moduleID = ModuleID.commands,
interfaceRole = InterfaceRole.RECEIVER,
versionsRepository = versionsRepository,
basePathOverride = basePathOverride
) {

override suspend fun doRegisterOn(transportServer: TransportServer) {
transportServer.handle(
method = HttpMethod.POST,
path = basePathSegments + FixedPathSegment("START_SESSION")
) { req ->
val senderPlatformUrl = httpAuth.partnerUrlFromRequest(req)
val startSession = mapper.readValue(req.body, StartSession::class.java)

req.httpResponse {
service.postStartSession(senderPlatformUrl, startSession)
}
}

transportServer.handle(
method = HttpMethod.POST,
path = basePathSegments + FixedPathSegment("STOP_SESSION")
) { req ->
val senderPlatformUrl = httpAuth.partnerUrlFromRequest(req)
val stopSession = mapper.readValue(req.body, StopSession::class.java)

req.httpResponse {
service.postStopSession(senderPlatformUrl, stopSession = stopSession)
}
}

transportServer.handle(
method = HttpMethod.POST,
path = basePathSegments + FixedPathSegment("RESERVE_NOW")
) { req ->
val senderPlatformUrl = httpAuth.partnerUrlFromRequest(req)
val reserveNow = mapper.readValue(req.body, ReserveNow::class.java)

req.httpResponse {
service.postReserveNow(senderPlatformUrl, reserveNow)
}
}

transportServer.handle(
method = HttpMethod.POST,
path = basePathSegments + FixedPathSegment("CANCEL_RESERVATION")
) { req ->
val senderPlatformUrl = httpAuth.partnerUrlFromRequest(req)
val cancelReservation = mapper.readValue(req.body, CancelReservation::class.java)

req.httpResponse {
service.postCancelReservation(senderPlatformUrl, cancelReservation)
}
}

transportServer.handle(
method = HttpMethod.POST,
path = basePathSegments + FixedPathSegment("UNLOCK_CONNECTOR")
) { req ->
val senderPlatformUrl = httpAuth.partnerUrlFromRequest(req)
val unlockConnector = mapper.readValue(req.body, UnlockConnector::class.java)

req.httpResponse {
service.postUnlockConnector(senderPlatformUrl, unlockConnector)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package com.izivia.ocpi.toolkit.modules.commands

import com.izivia.ocpi.toolkit.common.*
import com.izivia.ocpi.toolkit.modules.commands.domain.*
import com.izivia.ocpi.toolkit.modules.credentials.repositories.PartnerRepository
import com.izivia.ocpi.toolkit.modules.tokens.domain.Token
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
import java.time.Instant

class CommandEmspClient(
private val transportClientBuilder: TransportClientBuilder,
private val serverVersionsEndpointUrl: String,
private val partnerRepository: PartnerRepository,
private val callbackBaseUrl: String
) {

private suspend fun buildTransport(): TransportClient = transportClientBuilder
.buildFor(
module = ModuleID.commands,
partnerUrl = serverVersionsEndpointUrl,
partnerRepository = partnerRepository
)

suspend fun postStartSession(
token: Token,
locationId: CiString,
evseId: CiString?,
connectorId: CiString?,
authorizationReference: CiString
): OcpiResponseBody<CommandResponse> =
with(buildTransport()) {
send(
HttpRequest(
method = HttpMethod.POST,
path = "/START_SESSION",
body = mapper.writeValueAsString(
StartSession(
responseUrl = "$callbackBaseUrl/START_SESSION/callback/$authorizationReference",
token = token,
locationId = locationId,
evseUid = evseId,
connectorId = connectorId,
authorizationReference = authorizationReference
)
)
)
.withRequiredHeaders(
requestId = generateUUIDv4Token(),
correlationId = generateUUIDv4Token()
)
.authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl)
)
.parseBody()
}

suspend fun postStopSession(sessionId: String): OcpiResponseBody<CommandResponse> =
with(buildTransport()) {
send(
HttpRequest(
method = HttpMethod.POST,
path = "/STOP_SESSION",
body = mapper.writeValueAsString(
StopSession(
responseUrl = "$callbackBaseUrl/STOP_SESSION/callback/$sessionId",
sessionId = sessionId
)
)
)
.withRequiredHeaders(
requestId = generateUUIDv4Token(),
correlationId = generateUUIDv4Token()
)
.authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl)
)
.parseBody()
}

suspend fun postReserveNow(
token: Token,
expiryDate: Instant,
reservationId: CiString,
locationId: CiString,
evseUid: CiString?,
authorizationReference: CiString?
): OcpiResponseBody<CommandResponse> =
with(buildTransport()) {
send(
HttpRequest(
method = HttpMethod.POST,
path = "/RESERVE_NOW",
body = mapper.writeValueAsString(
ReserveNow(
responseUrl = "$callbackBaseUrl/RESERVE_NOW/callback/$reservationId",
token = token,
expiryDate = expiryDate,
reservationId = reservationId,
locationId = locationId,
evseUid = evseUid,
authorizationReference = authorizationReference
)
)
)
.withRequiredHeaders(
requestId = generateUUIDv4Token(),
correlationId = generateUUIDv4Token()
)
.authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl)
)
.parseBody()
}

suspend fun postCancelReservation(reservationId: CiString): OcpiResponseBody<CommandResponse> =
with(buildTransport()) {
send(
HttpRequest(
method = HttpMethod.POST,
path = "/CANCEL_RESERVATION",
body = mapper.writeValueAsString(
CancelReservation(
responseUrl = "$callbackBaseUrl/CANCEL_RESERVATION/callback/$reservationId",
reservationId = reservationId
)
)
)
.withRequiredHeaders(
requestId = generateUUIDv4Token(),
correlationId = generateUUIDv4Token()
)
.authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl)
)
.parseBody()
}

suspend fun postUnlockConnector(
locationId: CiString,
evseUid: CiString,
connectorId: CiString
): OcpiResponseBody<CommandResponse> =
with(buildTransport()) {
send(
HttpRequest(
method = HttpMethod.POST,
path = "/UNLOCK_CONNECTOR",
body = mapper.writeValueAsString(
UnlockConnector(
responseUrl =
"$callbackBaseUrl/UNLOCK_CONNECTOR/callback/$locationId/$evseUid/$connectorId",
locationId = locationId,
evseUid = evseUid,
connectorId = connectorId
)
)
)
.withRequiredHeaders(
requestId = generateUUIDv4Token(),
correlationId = generateUUIDv4Token()
)
.authenticate(partnerRepository = partnerRepository, partnerUrl = serverVersionsEndpointUrl)
)
.parseBody()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.izivia.ocpi.toolkit.modules.commands

import com.izivia.ocpi.toolkit.common.OcpiResponseBody
import com.izivia.ocpi.toolkit.modules.commands.domain.CommandResult

interface CommandEmspInterface {
suspend fun postCallbackStartSession(
authorizationReference: String,
result: CommandResult
): OcpiResponseBody<String>

suspend fun postCallbackStopSession(sessionId: String, result: CommandResult): OcpiResponseBody<String>

suspend fun postCallbackReserveNow(reservationId: String, result: CommandResult): OcpiResponseBody<String>

suspend fun postCallbackCancelReservation(reservationId: String, result: CommandResult): OcpiResponseBody<String>

suspend fun postCallbackUnlockConnector(
locationId: String,
evseId: String,
connectorId: String,
result: CommandResult
): OcpiResponseBody<String>
}
Loading
Loading