212 lines
5.6 KiB
Go
212 lines
5.6 KiB
Go
package vehicleconfig
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/tls"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"encoding/xml"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"fiskerinc.com/modules/common"
|
|
"fiskerinc.com/modules/common/staticerrors"
|
|
"fiskerinc.com/modules/logger"
|
|
"fiskerinc.com/modules/utils/envtool"
|
|
)
|
|
|
|
func NewSAPService() SAPServiceInterface {
|
|
return &SAPService{
|
|
sapUser: envtool.GetEnv("SAP_USER", "REPLACE_ME"),
|
|
sapPass: envtool.GetEnv("SAP_PASS", "REPLACE_ME"),
|
|
sapURL: envtool.GetEnv("SAP_URL", "REPLACE_ME"),
|
|
}
|
|
}
|
|
|
|
type SAPServiceInterface interface {
|
|
GetSAPOrder(vin string) (order common.VehicleOrder, err error)
|
|
GetECUVersions(vin string) (versions map[string]string, err error)
|
|
UpdateECUVersions(vin string, versions map[string]string) (err error)
|
|
GetFeatureCodes(vin string) (common.SAPResponse, error)
|
|
SubmitResult(vin string, success bool) (err error)
|
|
SubmitCarFlashpackVersion(vin string, previousFlashpack string, flashpack string) error
|
|
}
|
|
|
|
// This is currently a mock of the actual calls to the SAP and the ODX parser services
|
|
type SAPService struct {
|
|
sapUser string
|
|
sapPass string
|
|
sapURL string
|
|
}
|
|
|
|
type SAPSubmitResultBody struct {
|
|
VIN string `json:"vin" validate:"required,vin"`
|
|
DateTime string `json:"dateTime"`
|
|
Flag string `json:"flag"`
|
|
}
|
|
|
|
type SAPSubmitCarFlashpackVersionBody struct {
|
|
VIN string `json:"vin" validate:"required,vin"`
|
|
CreatedOn string `json:"createdOn"`
|
|
CurrentSWV string `json:"currentSWV"`
|
|
PreviousSWV string `json:"previousSWV"`
|
|
ModeOfSWU string `json:"modeOfSWU"`
|
|
}
|
|
|
|
// Returns the current vehicle order from SAP
|
|
func (cs *SAPService) GetSAPOrder(vin string) (order common.VehicleOrder, err error) {
|
|
var result common.OrderUpdated
|
|
err = xml.Unmarshal([]byte(vehicleOrder), &result)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
order = result.VehicleOrder
|
|
return
|
|
}
|
|
|
|
// Returns the current ECU versions for a car
|
|
func (cs *SAPService) GetECUVersions(vin string) (versions map[string]string, err error) {
|
|
versions = map[string]string{}
|
|
for _, ecu := range ecus {
|
|
versions[ecu] = "10000"
|
|
}
|
|
return
|
|
}
|
|
|
|
// Submits the updated ECU versions for a car back to SAP
|
|
func (cs *SAPService) UpdateECUVersions(vin string, versions map[string]string) (err error) {
|
|
|
|
return
|
|
}
|
|
|
|
func (cs *SAPService) GetFeatureCodes(vin string) (common.SAPResponse, error) {
|
|
headers := http.Header{}
|
|
headers.Add("Authorization", cs.createToken())
|
|
url := fmt.Sprintf("%s/vehicleSpec?vin=%s", cs.sapURL, vin)
|
|
|
|
request, err := http.NewRequest(http.MethodGet, url, nil)
|
|
if err != nil {
|
|
return common.SAPResponse{}, errors.WithStack(err)
|
|
}
|
|
|
|
client := createHttpClient()
|
|
request.Header = headers
|
|
resp, err := client.Do(request)
|
|
if err != nil {
|
|
return common.SAPResponse{}, errors.WithStack(err)
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return common.SAPResponse{}, errors.WithStack(fmt.Errorf(staticerrors.ErrorSAPFailedCall+" with status: %s", resp.Status))
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
respBody, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return common.SAPResponse{}, errors.WithStack(err)
|
|
}
|
|
|
|
respPayload := common.SAPResponse{}
|
|
err = json.Unmarshal(respBody, &respPayload)
|
|
if err != nil {
|
|
return common.SAPResponse{}, errors.WithStack(err)
|
|
}
|
|
|
|
return respPayload, nil
|
|
}
|
|
|
|
func (cs *SAPService) SubmitResult(vin string, success bool) (err error) {
|
|
headers := http.Header{}
|
|
headers.Add("Authorization", cs.createToken())
|
|
headers.Add("Content-Type", "application/json")
|
|
|
|
timeNow := time.Now()
|
|
formattedTime := timeNow.Format("2006-01-02T15:04:05")
|
|
|
|
body := &SAPSubmitResultBody{
|
|
VIN: vin,
|
|
DateTime: formattedTime,
|
|
Flag: "not completed",
|
|
}
|
|
if success {
|
|
body.Flag = "completed"
|
|
}
|
|
|
|
bodyJSON, err := json.Marshal(body)
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
return cs.submit("vehicleSpecAck", bodyJSON, http.StatusOK)
|
|
}
|
|
|
|
func (cs *SAPService) SubmitCarFlashpackVersion(vin string, previousFlashpack string, flashpack string) error {
|
|
timeNow := time.Now()
|
|
formattedTime := timeNow.Format("2006-01-02T15:04:05.999999Z")
|
|
body := &SAPSubmitCarFlashpackVersionBody{
|
|
VIN: vin,
|
|
CreatedOn: formattedTime,
|
|
PreviousSWV: previousFlashpack,
|
|
CurrentSWV: flashpack,
|
|
ModeOfSWU: "OTA",
|
|
}
|
|
|
|
bodyJSON, err := json.Marshal(body)
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
return cs.submit("swVersionUpdate", bodyJSON, http.StatusCreated)
|
|
}
|
|
|
|
func (cs *SAPService) submit(endpoint string, bodyJSON []byte, successStatus int) error {
|
|
headers := http.Header{}
|
|
headers.Add("Authorization", cs.createToken())
|
|
headers.Add("Content-Type", "application/json")
|
|
url := fmt.Sprintf("%s/%s", cs.sapURL, endpoint)
|
|
|
|
request, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(bodyJSON))
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
client := createHttpClient()
|
|
request.Header = headers
|
|
resp, err := client.Do(request)
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
if resp.StatusCode != successStatus {
|
|
return errors.WithStack(fmt.Errorf(staticerrors.ErrorSAPFailedCall+" with status: %s and payload: %s", resp.Status, string(bodyJSON)))
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
respBody, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
logger.Info().Msg(fmt.Sprintf("sap service has been notified at %s of car update: %s", endpoint, string(respBody)))
|
|
|
|
return nil
|
|
}
|
|
|
|
func (cs *SAPService) createToken() string {
|
|
token := base64.StdEncoding.EncodeToString([]byte(cs.sapUser + ":" + cs.sapPass))
|
|
return fmt.Sprintf("Basic %s", token)
|
|
}
|
|
|
|
func createHttpClient() http.Client {
|
|
transport := http.DefaultTransport.(*http.Transport)
|
|
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
|
return http.Client{
|
|
Transport: transport,
|
|
}
|
|
}
|