Skip to content
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
Expand Up @@ -12,6 +12,7 @@ import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.izivia.ocpi.toolkit.common.OcpiResponseBody
import com.izivia.ocpi.toolkit.integrations.jackson.mixins.*
import com.izivia.ocpi.toolkit.modules.hubclientinfo.domain.ConnectionStatus
import com.izivia.ocpi.toolkit.modules.locations.domain.*
import com.izivia.ocpi.toolkit.serialization.OcpiSerializer
import java.time.Instant
Expand Down Expand Up @@ -46,6 +47,7 @@ class JacksonOcpiSerializer : OcpiSerializer() {
.addMixIn(ParkingRestriction::class.java, ParkingRestrictionMixin::class.java)
.addMixIn(ParkingType::class.java, ParkingTypeMixin::class.java)
.addMixIn(Connector::class.java, ConnectorMixin::class.java)
.addMixIn(ConnectionStatus::class.java, ConnectionStatusMixin::class.java)

override val name: String = "jackson"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,8 @@ enum class ParkingTypeMixin {
@JsonEnumDefaultValue
OTHER,
}

enum class ConnectionStatusMixin {
@JsonEnumDefaultValue
OTHER,
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.izivia.ocpi.toolkit.modules.chargingProfiles.domain.*
import com.izivia.ocpi.toolkit.modules.commands.domain.*
import com.izivia.ocpi.toolkit.modules.credentials.domain.CredentialRole
import com.izivia.ocpi.toolkit.modules.credentials.domain.Credentials
import com.izivia.ocpi.toolkit.modules.hubclientinfo.domain.ClientInfo
import com.izivia.ocpi.toolkit.modules.locations.domain.*
import com.izivia.ocpi.toolkit.modules.sessions.domain.ChargingPreferences
import com.izivia.ocpi.toolkit.modules.sessions.domain.ChargingPreferencesPartial
Expand Down Expand Up @@ -135,6 +136,9 @@ import java.time.Instant
DisplayTextPartial::class,
Price::class,
PricePartial::class,

// HubClientInfo
ClientInfo::class,
],
customClassSerializers = [
// Built-in types
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.izivia.ocpi.toolkit.modules.hubclientinfo

import com.izivia.ocpi.toolkit.common.*
import com.izivia.ocpi.toolkit.modules.credentials.repositories.PartnerRepository
import com.izivia.ocpi.toolkit.modules.hubclientinfo.domain.ClientInfo
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 HubClientInfoReceiverClient(
private val transportClientBuilder: TransportClientBuilder,
private val partnerId: String,
private val partnerRepository: PartnerRepository,
) : HubClientInfoSenderInterface {

private suspend fun buildTransport(): TransportClient = transportClientBuilder
.buildFor(
module = ModuleID.hubclientinfo,
partnerId = partnerId,
partnerRepository = partnerRepository,
)

override suspend fun getAll(
dateFrom: Instant?,
dateTo: Instant?,
offset: Int,
limit: Int?,
): SearchResult<ClientInfo> = with(buildTransport()) {
send(
HttpRequest(
method = HttpMethod.GET,
queryParams = listOfNotNull(
dateFrom?.let { "date_from" to dateFrom.toString() },
dateTo?.let { "date_to" to dateTo.toString() },
"offset" to offset.toString(),
limit?.let { "limit" to limit.toString() },
).toMap(),
)
.withRequiredHeaders(
requestId = generateRequestId(),
correlationId = generateCorrelationId(),
)
.authenticate(partnerRepository = partnerRepository, partnerId = partnerId),
)
.parseSearchResult(offset)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.izivia.ocpi.toolkit.modules.hubclientinfo

import com.izivia.ocpi.toolkit.modules.hubclientinfo.domain.ClientInfo

/**
* Receiver Interface (usually CPO or eMSP)
*
* - GET: Retrieve a ClientInfo object as it is stored in the connected clients system.
* - POST: n/a
* - PUT: Push new/updated ClientInfo object to the connect client
* - PATCH: n/a
* - DELETE: n/a, Use PUT, ClientInfo objects cannot be removed).
*/
interface HubClientInfoReceiverInterface {
suspend fun get(countryCode: String, partyId: String): ClientInfo?
suspend fun put(countryCode: String, partyId: String, clientInfo: ClientInfo)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.izivia.ocpi.toolkit.modules.hubclientinfo

import com.izivia.ocpi.toolkit.common.OcpiSelfRegisteringModuleServer
import com.izivia.ocpi.toolkit.common.TimeProvider
import com.izivia.ocpi.toolkit.common.respondNothing
import com.izivia.ocpi.toolkit.common.respondObject
import com.izivia.ocpi.toolkit.modules.hubclientinfo.domain.ClientInfo
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.serialization.deserializeObject
import com.izivia.ocpi.toolkit.serialization.mapper
import com.izivia.ocpi.toolkit.transport.TransportServer
import com.izivia.ocpi.toolkit.transport.domain.HttpMethod
import com.izivia.ocpi.toolkit.transport.domain.VariablePathSegment
import java.time.Instant

class HubClientInfoReceiverServer(
private val service: HubClientInfoReceiverInterface,
private val timeProvider: TimeProvider = TimeProvider { Instant.now() },
versionsRepository: MutableVersionsRepository? = null,
basePathOverride: String? = null,
) : OcpiSelfRegisteringModuleServer(
ocpiVersion = VersionNumber.V2_2_1,
moduleID = ModuleID.hubclientinfo,
interfaceRole = InterfaceRole.RECEIVER,
versionsRepository = versionsRepository,
basePathOverride = basePathOverride,
) {

override suspend fun doRegisterOn(transportServer: TransportServer) {
transportServer.handle(
method = HttpMethod.GET,
path = basePathSegments + listOf(
VariablePathSegment("country_code"),
VariablePathSegment("party_id"),
),
) { req ->
req.respondObject(timeProvider.now()) {
service.get(
countryCode = req.pathParams["country_code"]!!,
partyId = req.pathParams["party_id"]!!,
)
}
}

transportServer.handle(
method = HttpMethod.PUT,
path = basePathSegments + listOf(
VariablePathSegment("country_code"),
VariablePathSegment("party_id"),
),
) { req ->
req.respondNothing(timeProvider.now()) {
service.put(
countryCode = req.pathParams["country_code"]!!,
partyId = req.pathParams["party_id"]!!,
clientInfo = mapper.deserializeObject<ClientInfo>(req.body!!),
)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.izivia.ocpi.toolkit.modules.hubclientinfo

import com.izivia.ocpi.toolkit.common.*
import com.izivia.ocpi.toolkit.modules.credentials.repositories.PartnerRepository
import com.izivia.ocpi.toolkit.modules.hubclientinfo.domain.ClientInfo
import com.izivia.ocpi.toolkit.modules.versions.domain.ModuleID
import com.izivia.ocpi.toolkit.serialization.mapper
import com.izivia.ocpi.toolkit.serialization.serializeObject
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 HubClientInfoSenderClient(
private val transportClientBuilder: TransportClientBuilder,
private val partnerId: String,
private val partnerRepository: PartnerRepository,
) : HubClientInfoReceiverInterface {

private suspend fun buildTransport(): TransportClient = transportClientBuilder
.buildFor(
module = ModuleID.hubclientinfo,
partnerId = partnerId,
partnerRepository = partnerRepository,
)

override suspend fun get(
countryCode: String,
partyId: String,
): ClientInfo? =
with(buildTransport()) {
send(
HttpRequest(
method = HttpMethod.GET,
path = "/$countryCode/$partyId",
)
.withRequiredHeaders(
requestId = generateRequestId(),
correlationId = generateCorrelationId(),
)
.authenticate(partnerRepository = partnerRepository, partnerId = partnerId),
)
.parseOptionalResult()
}

override suspend fun put(
countryCode: String,
partyId: String,
clientInfo: ClientInfo,
): Unit =
with(buildTransport()) {
send(
HttpRequest(
method = HttpMethod.PUT,
path = "/$countryCode/$partyId",
body = mapper.serializeObject(clientInfo),
)
.withRequiredHeaders(
requestId = generateRequestId(),
correlationId = generateCorrelationId(),
)
.authenticate(partnerRepository = partnerRepository, partnerId = partnerId),
)
.parseResultOrNull<Any>()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.izivia.ocpi.toolkit.modules.hubclientinfo

import com.izivia.ocpi.toolkit.common.SearchResult
import com.izivia.ocpi.toolkit.modules.hubclientinfo.domain.ClientInfo
import java.time.Instant

/**
* Sender Interface (usually HUB)
*
* - GET:Get the list of known ClientInfo objects, last updated between the {date_from} and {date_to} paginated)
* - POST: n/a
* - PUT: n/a
* - PATCH: n/a
* - DELETE: n/a
*/
interface HubClientInfoSenderInterface {

/**
* If additional parameters: {date_from} and/or {date_to} are provided, only ClientInfo objects with (last_updated)
* between the given {date_from} (including) and {date_to} (excluding) will be returned.
*
* This request is paginated, it supports the pagination related URL parameters.
*
* @param dateFrom Instant? Only return ClientInfo that have last_updated after or equal to this Date/Time
* (inclusive).
* @param dateTo Instant?Only return ClientInfo that have last_updated up to this Date/Time, but not including
* (exclusive).
* @param offset Int? The offset of the first object returned. Default is 0.
* @param limit Int? Maximum number of objects to GET.
* @return List<ClientInfo> The endpoint response with list of valid ClientInfo objects, the header will contain the
* pagination related headers. Any older information that is not specified in the response is considered as no
* longer valid. Each object must contain all required fields. Fields that are not specified may be considered as
* null values.
*/
suspend fun getAll(
dateFrom: Instant?,
dateTo: Instant?,
offset: Int,
limit: Int?,
): SearchResult<ClientInfo>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.izivia.ocpi.toolkit.modules.hubclientinfo

import com.izivia.ocpi.toolkit.common.OcpiSelfRegisteringModuleServer
import com.izivia.ocpi.toolkit.common.TimeProvider
import com.izivia.ocpi.toolkit.common.respondSearchResult
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 java.time.Instant

class HubClientInfoSenderServer(
private val service: HubClientInfoSenderInterface,
private val timeProvider: TimeProvider = TimeProvider { Instant.now() },
versionsRepository: MutableVersionsRepository? = null,
basePathOverride: String? = null,
) : OcpiSelfRegisteringModuleServer(
ocpiVersion = VersionNumber.V2_2_1,
moduleID = ModuleID.hubclientinfo,
interfaceRole = InterfaceRole.SENDER,
versionsRepository = versionsRepository,
basePathOverride = basePathOverride,
) {

override suspend fun doRegisterOn(transportServer: TransportServer) {
transportServer.handle(
method = HttpMethod.GET,
path = basePathSegments,
queryParams = listOf("date_from", "date_to", "offset", "limit"),
) { req ->
req.respondSearchResult(timeProvider.now()) {
val dateFrom = req.queryParams["date_from"]
val dateTo = req.queryParams["date_to"]

service.getAll(
dateFrom = dateFrom?.let { Instant.parse(it) },
dateTo = dateTo?.let { Instant.parse(it) },
offset = req.queryParams["offset"]?.toInt() ?: 0,
limit = req.queryParams["limit"]?.toInt(),
)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.izivia.ocpi.toolkit.modules.hubclientinfo.domain

import com.izivia.ocpi.toolkit.common.CiString
import com.izivia.ocpi.toolkit.modules.credentials.domain.Role
import java.time.Instant

/**
* @property countryCode Country code of the country this party is operating in, as used in the credentials exchange.
* @property partyId CPO or eMSP ID of this party (following the 15118 ISO standard), as used in the credentials exchange.
* @property role The role of the connected party.
* @property status Status of the connection to the party.
* @property lastUpdated Timestamp when this ClientInfo object was last updated.
*/
data class ClientInfo(
val countryCode: CiString,
val partyId: CiString,
val role: Role,
val status: ConnectionStatus,
val lastUpdated: Instant,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.izivia.ocpi.toolkit.modules.hubclientinfo.domain

enum class ConnectionStatus {
/**
* Party is connected.
*/
CONNECTED,

/**
* Party is currently not connected.
*/
OFFLINE,

/**
* Connection to this party is planned, but has never been connected.
*/
PLANNED,

/**
* Party is now longer active, will never connect anymore.
*/
SUSPENDED,

/**
* Placeholder entry serving as default value if we can not match with any other.
* Avoids failing deserialization on invalid entries.
*/
OTHER,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.izivia.ocpi.toolkit.modules.hubclientinfo.repositories

import com.izivia.ocpi.toolkit.modules.hubclientinfo.HubClientInfoReceiverInterface

interface HubClientInfoReceiverRepository : HubClientInfoReceiverInterface
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.izivia.ocpi.toolkit.modules.hubclientinfo.repositories

import com.izivia.ocpi.toolkit.modules.hubclientinfo.HubClientInfoSenderInterface

interface HubClientInfoSenderRepository : HubClientInfoSenderInterface
Loading