Skip to content

Commit

Permalink
Add models for Kamereon (#37)
Browse files Browse the repository at this point in the history
* Add models for RenaultClient

* Update fixtures

* Use marshmallow_dataclass

* Update fixtures

* Update fixtures

* Update fixtures

* Update fixtures

* Add KamereonVehicleDataResponse

* Add tests for vehicle actions

* Add tests for vehicle errors

* Fix tests

* Add KamereonException

* Fix tests

* Update parsing of Kamereon errors

* Add raw_data to Kamereon models

* Fix pre-commit

* Fix method descriptions

* Remove attributes property on KamereonVehicleData model
  • Loading branch information
epenet authored Nov 18, 2020
1 parent 9be7a35 commit ba1ead3
Show file tree
Hide file tree
Showing 38 changed files with 715 additions and 18 deletions.
16 changes: 16 additions & 0 deletions src/renault_api/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,19 @@ def __init__(self, error_code: int, error_details: Optional[str]):
"""Initialise GigyaResponseException."""
self.error_code = error_code
self.error_details = error_details


# Kamereon exceptions
class KamereonException(RenaultException):
"""Base exception for Kamereon errors."""

pass


class KamereonResponseException(KamereonException):
"""Kamereon returned a parsable errors."""

def __init__(self, error_code: Optional[str], error_details: Optional[str]):
"""Initialise KamereonResponseException."""
self.error_code = error_code
self.error_details = error_details
18 changes: 17 additions & 1 deletion src/renault_api/model/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
"""Models for Renault API."""
from dataclasses import dataclass
from typing import Any
from typing import Dict

import marshmallow


@dataclass
class BaseModel:
"""Base model for Gigya and Kamereon models to include raw_data."""

raw_data: Dict[str, Any]


class BaseSchema(marshmallow.Schema):
"""Base schema for Gigya models to exclude unknown fields."""
"""Base schema for Gigya and Kamereon models to exclude unknown fields."""

class Meta:
"""Force unknown fields to 'exclude'."""

unknown = marshmallow.EXCLUDE

@marshmallow.pre_load
def get_raw_data(self, data, **kwargs): # type: ignore
"""Ensure raw_data is added to the data set."""
return {"raw_data": data, **data}
117 changes: 117 additions & 0 deletions src/renault_api/model/kamereon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
"""Kamereon models."""
import json
from dataclasses import dataclass
from typing import List
from typing import Optional

import marshmallow_dataclass

from . import BaseModel
from . import BaseSchema
from renault_api.exceptions import KamereonResponseException


@dataclass
class KamereonResponseError(BaseModel):
"""Kamereon response error."""

errorCode: Optional[str] # noqa: N815
errorMessage: Optional[str] # noqa: N815

def raise_for_error_code(self) -> None:
"""Raise exception from response error."""
raise KamereonResponseException(self.errorCode, self.get_error_details())

def get_error_details(self) -> Optional[str]:
"""Extract the error details sometimes hidden inside nested JSON."""
try:
error_details = json.loads(self.errorMessage or "{}")
except json.JSONDecodeError:
return self.errorMessage

error_descriptions = []
for inner_error in error_details.get("errors", []):
error_description = " ".join(
filter(
None,
[
inner_error.get("title"),
inner_error.get("source", {}).get("pointer"),
inner_error.get("detail"),
],
)
)
error_descriptions.append(error_description)

return ", ".join(error_descriptions) or self.errorMessage


@dataclass
class KamereonResponse(BaseModel):
"""Kamereon response."""

errors: Optional[List[KamereonResponseError]]

def raise_for_error_code(self) -> None:
"""Raise exception if errors found in the response."""
for error in self.errors or []:
# Until we have a sample for multiple errors, just raise on first one
error.raise_for_error_code()


@dataclass
class KamereonPersonAccount(BaseModel):
"""Kamereon account data."""

accountId: Optional[str] # noqa: N815
accountType: Optional[str] # noqa: N815
accountStatus: Optional[str] # noqa: N815


@dataclass
class KamereonPersonResponse(KamereonResponse):
"""Kamereon response to GET on /persons/{gigya_person_id}."""

accounts: List[KamereonPersonAccount]


@dataclass
class KamereonVehiclesLink(BaseModel):
"""Kamereon account data."""

vin: Optional[str]


@dataclass
class KamereonVehiclesResponse(KamereonResponse):
"""Kamereon response to GET on /accounts/{account_id}/vehicles."""

accountId: Optional[str] # noqa: N815
country: Optional[str]
vehicleLinks: List[KamereonVehiclesLink] # noqa: N815


@dataclass
class KamereonVehicleData(BaseModel):
"""Kamereon account data."""

type: Optional[str]
id: Optional[str]


@dataclass
class KamereonVehicleDataResponse(KamereonResponse):
"""Kamereon response to GET/POST on .../cars/{vin}/{type}."""

data: Optional[KamereonVehicleData]


KamereonPersonResponseSchema = marshmallow_dataclass.class_schema(
KamereonPersonResponse, base_schema=BaseSchema
)()
KamereonVehiclesResponseSchema = marshmallow_dataclass.class_schema(
KamereonVehiclesResponse, base_schema=BaseSchema
)()
KamereonVehicleDataResponseSchema = marshmallow_dataclass.class_schema(
KamereonVehicleDataResponse, base_schema=BaseSchema
)()
1 change: 0 additions & 1 deletion tests/fixtures/kamereon/account_vehicles.txt

This file was deleted.

1 change: 0 additions & 1 deletion tests/fixtures/kamereon/errors/invalid_upstream.txt

This file was deleted.

1 change: 0 additions & 1 deletion tests/fixtures/kamereon/errors/not_supported.txt

This file was deleted.

1 change: 0 additions & 1 deletion tests/fixtures/kamereon/errors/quota_limit.txt

This file was deleted.

68 changes: 68 additions & 0 deletions tests/fixtures/kamereon/person.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
{
"personId": "person-id-1",
"type": "I",
"country": "FR",
"civility": "1",
"firstName": "firstName",
"lastName": "lastName",
"idp": {
"idpId": "idpId",
"idpType": "GIGYA",
"idpStatus": "ACTIVE",
"login": "email@email.com",
"loginType": "EMAIL",
"lastLoginDate": "2020-11-16T11:43:52Z",
"termsConditionAcceptance": true,
"termsConditionLastAcceptanceDate": "2019-04-29T20:39:37.153608Z"
},
"emails": [
{
"emailType": "MAIN",
"emailValue": "email@email.com",
"validityFlag": true,
"createdDate": "2019-04-29T20:39:37.162651Z",
"lastModifiedDate": "2019-04-29T20:39:37.162654Z",
"functionalCreationDate": "2019-04-29T20:39:37.162651Z",
"functionalModificationDate": "2019-04-29T20:39:37.162654Z"
}
],
"phones": [
{
"phoneType": "MOBILE",
"phoneValue": "phoneValue",
"areaCode": "33",
"createdDate": "2019-06-17T09:47:14.745555Z",
"lastModifiedDate": "2019-06-17T09:47:14.745730Z",
"functionalCreationDate": "2019-06-17T09:47:14.745555Z",
"functionalModificationDate": "2019-06-17T09:47:14.745730Z"
}
],
"identities": [],
"myrRequest": false,
"accounts": [
{
"accountId": "account-id-1",
"accountType": "MYRENAULT",
"accountStatus": "ACTIVE",
"country": "FR",
"personId": "person-id-1",
"relationType": "OWNER"
},
{
"accountId": "account-id-2",
"externalId": "externalId",
"accountType": "SFDC",
"accountStatus": "ACTIVE",
"country": "FR",
"personId": "person-id-1",
"relationType": "OWNER"
}
],
"partyId": "partyId",
"mdmId": "mdmId",
"createdDate": "2019-04-29T20:39:37.163635Z",
"lastModifiedDate": "2020-11-16T11:43:57.658639Z",
"functionalCreationDate": "2019-04-29T20:39:37.162578Z",
"functionalModificationDate": "2020-11-16T11:29:48.631544Z",
"locale": "fr-FR"
}
1 change: 0 additions & 1 deletion tests/fixtures/kamereon/persons_detail.txt

This file was deleted.

1 change: 0 additions & 1 deletion tests/fixtures/kamereon/vehicle/battery-status.1.txt

This file was deleted.

1 change: 0 additions & 1 deletion tests/fixtures/kamereon/vehicle/battery-status.2.txt

This file was deleted.

1 change: 0 additions & 1 deletion tests/fixtures/kamereon/vehicle/charge-history.txt

This file was deleted.

1 change: 0 additions & 1 deletion tests/fixtures/kamereon/vehicle/charges.txt

This file was deleted.

1 change: 0 additions & 1 deletion tests/fixtures/kamereon/vehicle/cockpit.txt

This file was deleted.

1 change: 0 additions & 1 deletion tests/fixtures/kamereon/vehicle/hvac-status.txt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"data": {
"type": "ChargeMode",
"id": "guid",
"attributes": { "action": "schedule_mode" }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"data": {
"type": "ChargeSchedule",
"id": "guid",
"attributes": { "schedules": [] }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"data": {
"type": "ChargingStart",
"id": "guid",
"attributes": { "action": "start" }
}
}
7 changes: 7 additions & 0 deletions tests/fixtures/kamereon/vehicle_action/hvac-start.cancel.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"data": {
"type": "HvacStart",
"id": "guid",
"attributes": { "action": "cancel" }
}
}
7 changes: 7 additions & 0 deletions tests/fixtures/kamereon/vehicle_action/hvac-start.start.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"data": {
"type": "HvacStart",
"id": "guid",
"attributes": { "action": "start", "targetTemperature": 21.0 }
}
}

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

15 changes: 15 additions & 0 deletions tests/fixtures/kamereon/vehicle_data/battery-status.1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"data": {
"type": "Car",
"id": "VF1AAAAA555777999",
"attributes": {
"timestamp": "2020-11-17T09:06:48+01:00",
"batteryLevel": 50,
"batteryAutonomy": 128,
"batteryCapacity": 0,
"batteryAvailableEnergy": 0,
"plugStatus": 0,
"chargingStatus": -1.0
}
}
}
16 changes: 16 additions & 0 deletions tests/fixtures/kamereon/vehicle_data/battery-status.2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"data": {
"type": "Car",
"id": "VF1AAAAA555777999",
"attributes": {
"timestamp": "2020-11-09T07:41:49+01:00",
"batteryLevel": 34,
"batteryTemperature": 10,
"batteryAutonomy": 88,
"batteryCapacity": 0,
"batteryAvailableEnergy": 0,
"plugStatus": 0,
"chargingStatus": -1.0
}
}
}
16 changes: 16 additions & 0 deletions tests/fixtures/kamereon/vehicle_data/charge-history.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"data": {
"type": "Car",
"id": "VF1AAAAA555777999",
"attributes": {
"chargeSummaries": [
{
"month": "202011",
"totalChargesNumber": 1,
"totalChargesDuration": 479,
"totalChargesErrors": 0
}
]
}
}
}
21 changes: 21 additions & 0 deletions tests/fixtures/kamereon/vehicle_data/charges.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"data": {
"type": "Car",
"id": "VF1AAAAA555777999",
"attributes": {
"charges": [
{
"chargeStartDate": "2020-11-11T00:31:03Z",
"chargeEndDate": "2020-11-11T08:30:17Z",
"chargeDuration": 479,
"chargeStartBatteryLevel": 15,
"chargeEndBatteryLevel": 74,
"chargeBatteryLevelRecovered": 59,
"chargePower": "slow",
"chargeStartInstantaneousPower": 3100,
"chargeEndStatus": "ok"
}
]
}
}
}
7 changes: 7 additions & 0 deletions tests/fixtures/kamereon/vehicle_data/cockpit.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"data": {
"type": "Car",
"id": "VF1AAAAA555777999",
"attributes": { "totalMileage": 49114.27 }
}
}
7 changes: 7 additions & 0 deletions tests/fixtures/kamereon/vehicle_data/hvac-status.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"data": {
"type": "Car",
"id": "VF1AAAAA555777999",
"attributes": { "externalTemperature": 8.0, "hvacStatus": "off" }
}
}
Loading

0 comments on commit ba1ead3

Please sign in to comment.