Fully removed gabs dependency
All checks were successful
Golan Testing / testing (1.24.x, ubuntu-latest) (push) Successful in 26s

This commit is contained in:
2025-06-01 11:26:26 -04:00
parent 2542993b9f
commit df5ef6ed0d

373
client.go
View File

@ -9,7 +9,6 @@ import (
"time" "time"
"git.savin.nyc/alex/mysubaru/config" "git.savin.nyc/alex/mysubaru/config"
"github.com/Jeffail/gabs/v2"
"resty.dev/v3" "resty.dev/v3"
) )
@ -172,10 +171,19 @@ func (c *Client) validateSession() bool {
// } // }
reqURL := MOBILE_API_VERSION + apiURLs["API_VALIDATE_SESSION"] reqURL := MOBILE_API_VERSION + apiURLs["API_VALIDATE_SESSION"]
resp := c.execute(reqURL, GET, map[string]string{}, "", false) resp := c.execute(reqURL, GET, map[string]string{}, "", false)
c.logger.Debug("http request output", "request", "GetVehicleStatus", "body", resp)
if !c.isResponseSuccessfull(resp) { var r Response
err := json.Unmarshal(resp, &r)
if err != nil {
c.logger.Error("error while parsing json", "request", "GetClimatePresets", "error", err.Error())
}
if !r.Success {
return false return false
} }
return true
// result = False // result = False
// js_resp = await self.__open(API_VALIDATE_SESSION, GET) // js_resp = await self.__open(API_VALIDATE_SESSION, GET)
// _LOGGER.debug(pprint.pformat(js_resp)) // _LOGGER.debug(pprint.pformat(js_resp))
@ -193,10 +201,6 @@ func (c *Client) validateSession() bool {
// # New session cookie. Must call selectVehicle.json before any other API call. // # New session cookie. Must call selectVehicle.json before any other API call.
// if await self._select_vehicle(vin): // if await self._select_vehicle(vin):
// result = True // result = True
c.logger.Debug("session validation", "body", string([]byte(resp)))
return true
} }
// New function creates a New MySubaru client // New function creates a New MySubaru client
@ -224,44 +228,59 @@ func New(config *config.Config) (*Client, error) {
client.httpClient = httpClient client.httpClient = httpClient
resp := client.auth() resp := client.auth()
respParsed, err := gabs.ParseJSON(resp) var r Response
err := json.Unmarshal(resp, &r)
if err != nil { if err != nil {
client.logger.Error("error while parsing json", "request", "auth", "error", err.Error()) client.logger.Error("error while parsing json", "request", "auth", "error", err.Error())
} }
if client.isResponseSuccessfull(resp) { if r.Success {
client.logger.Debug("Client authentication successful", "isRegistered", respParsed.Path("data.deviceRegistered").Data().(bool)) var sd SessionData
client.isAuthenticated = true err := json.Unmarshal(r.Data, &sd)
client.isRegistered = respParsed.Path("data.deviceRegistered").Data().(bool) if err != nil {
} else { client.logger.Error("error while parsing json", "request", "auth", "error", err.Error())
error, _ := respParsed.Path("errorCode").Data().(string)
switch {
case error == apiErrors["ERROR_INVALID_ACCOUNT"]:
client.logger.Debug("Invalid account")
case error == apiErrors["ERROR_INVALID_CREDENTIALS"]:
client.logger.Debug("Client authentication failed")
case error == apiErrors["ERROR_PASSWORD_WARNING"]:
client.logger.Debug("Multiple Password Failures.")
default:
client.logger.Debug("Uknown error")
}
} }
client.logger.Debug("unmarshaled json data", "request", "auth", "type", "sessionData", "body", sd)
if !client.isRegistered { if client.isRegistered {
client.logger.Debug("Client authentication successful", "isRegistered", sd.DeviceRegistered)
client.isAuthenticated = true
client.isRegistered = sd.DeviceRegistered
} else {
client.registerDevice() client.registerDevice()
} }
// TODO: create a list of the car with currently present data client.logger.Debug("parcing cars assigned to the account", "quantity", len(sd.Vehicles))
client.logger.Debug("parcing cars assigned to the account", "quantity", len(respParsed.Path("data.vehicles").Children())) if len(sd.Vehicles) > 0 {
for _, vehicle := range respParsed.Path("data.vehicles").Children() { for _, vehicle := range sd.Vehicles {
client.logger.Debug("parsing car", "vin", vehicle.Path("vin").Data().(string)) client.logger.Debug("parsing car", "vin", vehicle.Vin)
client.listOfVins = append(client.listOfVins, vehicle.Path("vin").Data().(string)) client.listOfVins = append(client.listOfVins, vehicle.Vin)
}
client.currentVin = client.listOfVins[0]
} else {
client.logger.Error("there no cars assigned to the account")
return nil, err
}
} else {
// TODO: Work on errors
// error, _ := respParsed.Path("errorCode").Data().(string)
// switch {
// case error == apiErrors["ERROR_INVALID_ACCOUNT"]:
// client.logger.Debug("Invalid account")
// case error == apiErrors["ERROR_INVALID_CREDENTIALS"]:
// client.logger.Debug("Client authentication failed")
// case error == apiErrors["ERROR_PASSWORD_WARNING"]:
// client.logger.Debug("Multiple Password Failures.")
// default:
// client.logger.Debug("Uknown error")
// }
client.logger.Error("request was not successfull", "request", "auth", "error", err.Error())
return nil, err
} }
client.currentVin = respParsed.Path("data.vehicles.0.vin").Data().(string)
return client, nil return client, nil
} }
// SelectVehicle .
func (c *Client) SelectVehicle(vin string) VehicleData { func (c *Client) SelectVehicle(vin string) VehicleData {
// API > json > dataName > vehicle // API > json > dataName > vehicle
if vin == "" { if vin == "" {
@ -325,52 +344,9 @@ func (c *Client) SelectVehicle(vin string) VehicleData {
func (c *Client) GetVehicles() []*Vehicle { func (c *Client) GetVehicles() []*Vehicle {
var vehicles []*Vehicle var vehicles []*Vehicle
for _, vin := range c.listOfVins { for _, vin := range c.listOfVins {
params := map[string]string{ vehicle := c.GetVehicleByVIN(vin)
"vin": vin,
"_": timestamp()}
reqURL := MOBILE_API_VERSION + apiURLs["API_SELECT_VEHICLE"]
resp := c.execute(reqURL, GET, params, "", false)
c.logger.Debug("http request output", "request", "GetVehicles", "body", resp)
respParsed, err := gabs.ParseJSON(resp)
if err != nil {
c.logger.Error("error while parsing json", "request", "GetVehicles", "error", err.Error())
}
c.logger.Debug("http request output", "request", "GetVehicles", "body", respParsed)
vData := VehicleData{}
vdString := respParsed.Path("data").String()
json.Unmarshal([]byte(vdString), &vData)
vehicle := &Vehicle{
Vin: vin,
CarName: vData.VehicleName,
CarNickname: vData.Nickname,
ModelName: vData.ModelName,
ModelYear: vData.ModelYear,
ModelCode: vData.ModelCode,
ExtDescrip: vData.ExtDescrip,
IntDescrip: vData.IntDescrip,
TransCode: vData.TransCode,
EngineSize: vData.EngineSize,
VehicleKey: vData.VehicleKey,
LicensePlate: vData.LicensePlate,
LicensePlateState: vData.LicensePlateState,
Features: vData.Features,
SubscriptionFeatures: vData.SubscriptionFeatures,
client: c,
}
vehicle.GetVehicleStatus()
vehicle.GetVehicleCondition()
vehicle.GetVehicleHealth()
vehicle.GetClimatePresets()
vehicle.GetClimateUserPresets()
vehicle.GetClimateQuickPresets()
vehicles = append(vehicles, vehicle) vehicles = append(vehicles, vehicle)
} }
return vehicles return vehicles
} }
@ -383,33 +359,38 @@ func (c *Client) GetVehicleByVIN(vin string) *Vehicle {
"_": timestamp()} "_": timestamp()}
reqURL := MOBILE_API_VERSION + apiURLs["API_SELECT_VEHICLE"] reqURL := MOBILE_API_VERSION + apiURLs["API_SELECT_VEHICLE"]
resp := c.execute(reqURL, GET, params, "", false) resp := c.execute(reqURL, GET, params, "", false)
c.logger.Debug("http request output", "request", "GetVehicleByVIN", "body", resp)
respParsed, err := gabs.ParseJSON([]byte(resp)) var r Response
err := json.Unmarshal(resp, &r)
if err != nil { if err != nil {
c.logger.Error("error while parsing json", "request", "GetVehicleByVIN", "error", err.Error()) c.logger.Error("error while parsing json", "request", "GetVehicleByVIN", "error", err.Error())
} }
c.logger.Debug("http request output", "request", "GetVehicleByVIN", "body", respParsed)
vData := VehicleData{} if r.Success {
vdString := respParsed.Path("data").String() var vd VehicleData
json.Unmarshal([]byte(vdString), &vData) err = json.Unmarshal(r.Data, &vd)
if err != nil {
c.logger.Error("error while parsing json", "request", "GetVehicleByVIN", "error", err.Error())
}
c.logger.Debug("http request output", "request", "GetVehicleByVIN", "body", resp)
vehicle = &Vehicle{ vehicle = &Vehicle{
Vin: vin, Vin: vin,
CarName: vData.VehicleName, CarName: vd.VehicleName,
CarNickname: vData.Nickname, CarNickname: vd.Nickname,
ModelName: vData.ModelName, ModelName: vd.ModelName,
ModelYear: vData.ModelYear, ModelYear: vd.ModelYear,
ModelCode: vData.ModelCode, ModelCode: vd.ModelCode,
ExtDescrip: vData.ExtDescrip, ExtDescrip: vd.ExtDescrip,
IntDescrip: vData.IntDescrip, IntDescrip: vd.IntDescrip,
TransCode: vData.TransCode, TransCode: vd.TransCode,
EngineSize: vData.EngineSize, EngineSize: vd.EngineSize,
VehicleKey: vData.VehicleKey, VehicleKey: vd.VehicleKey,
LicensePlate: vData.LicensePlate, LicensePlate: vd.LicensePlate,
LicensePlateState: vData.LicensePlateState, LicensePlateState: vd.LicensePlateState,
Features: vData.Features, Features: vd.Features,
SubscriptionFeatures: vData.SubscriptionFeatures, SubscriptionFeatures: vd.SubscriptionFeatures,
client: c, client: c,
} }
vehicle.Doors = make(map[string]Door) vehicle.Doors = make(map[string]Door)
@ -423,109 +404,13 @@ func (c *Client) GetVehicleByVIN(vin string) *Vehicle {
vehicle.GetClimatePresets() vehicle.GetClimatePresets()
vehicle.GetClimateUserPresets() vehicle.GetClimateUserPresets()
vehicle.GetClimateQuickPresets() vehicle.GetClimateQuickPresets()
}
return vehicle return vehicle
} }
// GetVehicleStatus .
func (c *Client) GetVehicleStatus() {
// {
// "dataName": null,
// "errorCode": null,
// "success": true,
// "data": {
// "avgFuelConsumptionLitersPer100Kilometers": 12.5,
// "avgFuelConsumptionMpg": 18.8,
// "distanceToEmptyFuelKilometers": 563,
// "distanceToEmptyFuelKilometers10s": 560,
// "distanceToEmptyFuelMiles": 349.83,
// "distanceToEmptyFuelMiles10s": 350,
// "evDistanceToEmptyByStateKilometers": null,
// "evDistanceToEmptyByStateMiles": null,
// "evDistanceToEmptyKilometers": null,
// "evDistanceToEmptyMiles": null,
// "evStateOfChargePercent": null,
// "eventDate": 1640494569000,
// "eventDateStr": "2021-12-26T04:56+0000",
// "latitude": 40.700192,
// "longitude": -74.401377,
// "odometerValue": 24065,
// "odometerValueKilometers": 38721,
// "positionHeadingDegree": "150",
// "tirePressureFrontLeft": "2413",
// "tirePressureFrontLeftPsi": "35",
// "tirePressureFrontRight": "2413",
// "tirePressureFrontRightPsi": "35",
// "tirePressureRearLeft": "2551",
// "tirePressureRearLeftPsi": "37",
// "tirePressureRearRight": "2482",
// "tirePressureRearRightPsi": "36",
// "vehicleStateType": "IGNITION_OFF",
// "vhsId": 923920223
// }
// }
reqURL := MOBILE_API_VERSION + apiURLs["API_VEHICLE_STATUS"]
resp := c.execute(reqURL, GET, map[string]string{}, "", false)
respParsed, err := gabs.ParseJSON(resp)
if err != nil {
c.logger.Error("error while parsing json", "request", "GetVehicleStatus", "error", err.Error())
} }
c.logger.Debug("GET VEHICLE STATUS OUTPUT", "body", respParsed) c.logger.Error("error while parsing json", "request", "GetVehicleByVIN")
return &Vehicle{}
success, ok := respParsed.Path("success").Data().(bool)
// value == string, ok == false
if !ok {
// TODO: Work with errorCode
panic(success)
} }
}
// // GetClimateSettings .
// func (c *Client) GetClimateSettings() {
// // {
// // "success": true,
// // "errorCode": null,
// // "dataName": null,
// // "data": {
// // "climateZoneFrontTemp": "70",
// // "runTimeMinutes": "10",
// // "climateZoneFrontAirMode": "WINDOW",
// // "heatedSeatFrontLeft": "LOW_HEAT",
// // "heatedSeatFrontRight": "LOW_HEAT",
// // "heatedRearWindowActive": "true",
// // "climateZoneFrontAirVolume": "6",
// // "outerAirCirculation": "outsideAir",
// // "airConditionOn": "false",
// // "startConfiguration": "START_ENGINE_ALLOW_KEY_IN_IGNITION"
// // }
// // }
// reqURL := MOBILE_API_VERSION + apiURLs["API_G2_FETCH_CLIMATE_SETTINGS"]
// resp := c.execute(reqURL, GET, map[string]string{}, "", false)
// respParsed, err := gabs.ParseJSON(resp)
// if err != nil {
// c.logger.Error("error while parsing json", "request", "GetClimateSettings", "error", err.Error())
// }
// c.logger.Debug("CLIMATE SETTINGS OUTPUT", "response", respParsed)
// // ONLY FOR THAT REQUEST BECAUSE OF API SENDS BACK ESCAPING DATA IN DATA FIELD
// data, ok := respParsed.Path("data").Data().(string)
// // rawIn := json.RawMessage(in)
// // bytes, err := rawIn.MarshalJSON()
// // if err != nil {
// // panic(err)
// // }
// // value == string, ok == false
// if !ok {
// // TODO: Work with errorCode
// panic(data)
// }
// c.logger.Debug("CLIMATE SETTINGS OUTPUT", "body", data)
// }
// func isPINRequired() {} // func isPINRequired() {}
// func getVehicles() {} // func getVehicles() {}
@ -541,7 +426,7 @@ func (c *Client) GetVehicleStatus() {
// func fetch() {} // func fetch() {}
// Exec method executes a Client instance with the API URL // Exec method executes a Client instance with the API URL
func (c *Client) execute(requestUrl string, method string, params map[string]string, pollingUrl string, json bool) []byte { func (c *Client) execute(requestUrl string, method string, params map[string]string, pollingUrl string, j bool) []byte {
defer timeTrack("[TIMETRK] Executing Get Request") defer timeTrack("[TIMETRK] Executing Get Request")
var resp *resty.Response var resp *resty.Response
@ -557,7 +442,7 @@ func (c *Client) execute(requestUrl string, method string, params map[string]str
// POST Requests // POST Requests
if method == "POST" { if method == "POST" {
if json { if j {
// POST > JSON Body // POST > JSON Body
resp, _ = c.httpClient. resp, _ = c.httpClient.
R(). R().
@ -576,26 +461,23 @@ func (c *Client) execute(requestUrl string, method string, params map[string]str
c.logger.Error("error while getting body", "error", err.Error()) c.logger.Error("error while getting body", "error", err.Error())
} }
respParsed, err := gabs.ParseJSON(resBytes) var r Response
err = json.Unmarshal(resBytes, &r)
if err != nil { if err != nil {
c.logger.Error("error while parsing json", "request", "execute", "method", method, "url", requestUrl, "error", err.Error()) c.logger.Error("error while parsing json", "request", "execute", "method", method, "url", requestUrl, "error", err.Error())
} }
// c.logger.Debug("HTTP OUTPUT", "body", string(resBytes)) if !r.Success {
c.logger.Error("request is not successfull", "request", "execute", "method", method, "url", requestUrl, "error", err.Error())
_, ok := respParsed.Path("success").Data().(bool) } else {
// value == string, ok == false var sr ServiceRequest
if !ok { err := json.Unmarshal(r.Data, &sr)
// TODO: Work with errorCode if err != nil {
// panic(success) c.logger.Error("error while parsing json", "request", "HTTP POLLING", "error", err.Error())
fmt.Printf("ERROR: %+v", string(resBytes))
} }
if pollingUrl != "" { if pollingUrl != "" {
serviceRequestId, _ := respParsed.Path("data.serviceRequestId").Data().(string)
time.Sleep(3 * time.Second) time.Sleep(3 * time.Second)
attempts := 20 attempts := 20
poolingLoop: poolingLoop:
for attempts > 0 { for attempts > 0 {
@ -603,30 +485,31 @@ func (c *Client) execute(requestUrl string, method string, params map[string]str
SetBaseURL(MOBILE_API_SERVER[c.country]). SetBaseURL(MOBILE_API_SERVER[c.country]).
R(). R().
SetQueryParams(map[string]string{ SetQueryParams(map[string]string{
"serviceRequestId": serviceRequestId, "serviceRequestId": *sr.ServiceRequestID,
}). }).
Get(pollingUrl) Get(pollingUrl)
resBytes, _ := io.ReadAll(resp.Body) resBytes, _ := io.ReadAll(resp.Body)
c.logger.Debug("POLLING HTTP OUTPUT", "body", string(resBytes)) c.logger.Debug("POLLING HTTP OUTPUT", "body", string(resBytes))
// {"success":false,"errorCode":"404-soa-unableToParseResponseBody","dataName":"errorResponse","data":{"errorLabel":"404-soa-unableToParseResponseBody","errorDescription":null}} // {"success":false,"errorCode":"404-soa-unableToParseResponseBody","dataName":"errorResponse","data":{"errorLabel":"404-soa-unableToParseResponseBody","errorDescription":null}}
respParsed, err := gabs.ParseJSON(resBytes) var r Response
err := json.Unmarshal(resBytes, &r)
if err != nil { if err != nil {
panic(err) c.logger.Error("error while parsing json", "request", "HTTP POLLING", "error", err.Error())
} }
success, ok := respParsed.Path("success").Data().(bool) if r.Success {
if !ok { var sr ServiceRequest
panic(success) err := json.Unmarshal(r.Data, &sr)
if err != nil {
c.logger.Error("error while parsing json", "request", "HTTP POLLING", "error", err.Error())
} }
if success {
status, _ := respParsed.Path("data.remoteServiceState").Data().(string)
switch { switch {
case status == "finished": case sr.RemoteServiceState == "finished":
c.logger.Debug("Remote service request completed successfully", "request id", serviceRequestId) c.logger.Debug("Remote service request completed successfully", "request id", sr.ServiceRequestID)
break poolingLoop break poolingLoop
case status == "started": case sr.RemoteServiceState == "started":
c.logger.Debug("Subaru API reports remote service request is in progress", "request id", serviceRequestId) c.logger.Debug("Subaru API reports remote service request is in progress", "request id", sr.ServiceRequestID)
} }
} else { } else {
c.logger.Debug("Backend session expired, please try again") c.logger.Debug("Backend session expired, please try again")
@ -637,34 +520,36 @@ func (c *Client) execute(requestUrl string, method string, params map[string]str
} }
} }
}
return resBytes return resBytes
} }
// isResponseSuccessfull . // // isResponseSuccessfull .
func (c *Client) isResponseSuccessfull(resp []byte) bool { // func (c *Client) isResponseSuccessfull(resp []byte) bool {
respParsed, err := gabs.ParseJSON(resp) // respParsed, err := gabs.ParseJSON(resp)
if err != nil { // if err != nil {
c.logger.Debug("error while parsing json response", "error", err) // c.logger.Debug("error while parsing json response", "error", err)
}
success, ok := respParsed.Path("success").Data().(bool)
if !ok {
c.logger.Debug("response is not successful", "error", resp)
}
// ERRORS FROM CLIENT CREATION AFTER AUTH
// error, _ := respParsed.Path("errorCode").Data().(string)
// switch {
// case error == apiErrors["ERROR_INVALID_ACCOUNT"]:
// fmt.Println("Invalid account")
// case error == apiErrors["ERROR_INVALID_CREDENTIALS"]:
// {"success":false,"errorCode":"InvalidCredentials","dataName":"remoteServiceStatus","data":{"serviceRequestId":null,"success":false,"cancelled":false,"remoteServiceType":null,"remoteServiceState":null,"subState":null,"errorCode":null,"result":null,"updateTime":null,"vin":null,"errorDescription":"The credentials supplied are invalid, tries left 2"}}
// fmt.Println("Client authentication failed")
// case error == apiErrors["ERROR_PASSWORD_WARNING"]:
// fmt.Println("Multiple Password Failures.")
// default:
// fmt.Println("Uknown error")
// } // }
return success // success, ok := respParsed.Path("success").Data().(bool)
} // if !ok {
// c.logger.Debug("response is not successful", "error", resp)
// }
// // ERRORS FROM CLIENT CREATION AFTER AUTH
// // error, _ := respParsed.Path("errorCode").Data().(string)
// // switch {
// // case error == apiErrors["ERROR_INVALID_ACCOUNT"]:
// // fmt.Println("Invalid account")
// // case error == apiErrors["ERROR_INVALID_CREDENTIALS"]:
// // {"success":false,"errorCode":"InvalidCredentials","dataName":"remoteServiceStatus","data":{"serviceRequestId":null,"success":false,"cancelled":false,"remoteServiceType":null,"remoteServiceState":null,"subState":null,"errorCode":null,"result":null,"updateTime":null,"vin":null,"errorDescription":"The credentials supplied are invalid, tries left 2"}}
// // fmt.Println("Client authentication failed")
// // case error == apiErrors["ERROR_PASSWORD_WARNING"]:
// // fmt.Println("Multiple Password Failures.")
// // default:
// // fmt.Println("Uknown error")
// // }
// return success
// }