package tmobile import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "strings" "time" errorsO "errors" "fiskerinc.com/modules/grpc/sms" tmtg "fiskerinc.com/modules/tmobtokengen" "fiskerinc.com/modules/utils/envtool" "github.com/pkg/errors" ) type TMobileMiniClient struct { client httpClienter tg tmtg.Generator accessToken string baseURL string } // Meant to be used within 30 minutes, and then disposed of func NewTMobileMiniClient() (client *TMobileMiniClient, err error) { client = &TMobileMiniClient{} tg, err := InitTokenGen( envtool.GetEnv("TMOBILE_PRIVATE_KEY", ""), envtool.GetEnv("TMOBILE_CLIENT_ID", ""), envtool.GetEnv("TMOBILE_SECRET", ""), ) if err != nil { return } client.tg = tg client.baseURL = Endpoint client.client = &http.Client{ Timeout: time.Minute, } acsToken, err := client.AccessToken(context.Background()) if err != nil { return } client.SetAccessToken(acsToken.AccessToken) return } // AccessToken implements TMobClienter. func (tmc *TMobileMiniClient) AccessToken(ctx context.Context) (out *AccessTokenResponse, err error) { path := "/oauth2/v1/tokens" emap := make(tmtg.EHTSMap).SetAuthorization(tmc.tg.ClientSecretAsAuthVal()). SetURI(path). SetHTTPMethod(http.MethodPost) token, err := tmc.tg.Generate(emap) if err != nil { return nil, err } fullPath := tmc.baseURL + path req, err := http.NewRequestWithContext(ctx, http.MethodPost, fullPath, nil) if err != nil { return nil, errors.WithMessagef(ErrCreateRequest, "failed to create request") } req.Header.Set(Authorization, tmc.tg.ClientSecretAsAuthVal()) req.Header.Set(XAuthorization, token) err = tmc.do(req, &out) if err != nil { return nil, errors.WithMessage(err, failedToDoRequest) } return } // HandleChangeDeviceStatus implements TMobClienter. func (tmc *TMobileMiniClient) ChangeDeviceStatus(ctx context.Context, cda ChangeDeviceActivation) (err error) { var fn func(ctx context.Context, in *ICCIDBody)(out *ICCIDBody, err error) if cda.Enabled { fn = tmc.handleServiceRestore }else { fn = tmc.handleServiceCancel } for _, iccid := range cda.ICCIDs { iccid = strings.TrimSuffix(strings.TrimSuffix(iccid, "F"), "f") temp := ICCIDBody{ ICCID: iccid, } _, tErr := fn(ctx, &temp) if tErr != nil { err = errorsO.Join(err, tErr) } } return err } // ChangeRatePlan implements TMobClienter. func (tmc *TMobileMiniClient) ChangeRatePlan(context.Context, *ChangeRatePlanRequest) (*ChangeRatePlanResponse, error) { panic("unimplemented") } // CustomAttributes implements TMobClienter. func (tmc *TMobileMiniClient) CustomAttributes(ctx context.Context, in *CustomAtributesRequest) (*CustomAtributesResponse, error) { path := "/eitcsr-iotcp-line-of-service-v1/prd02/iotcp/v1/line-of-service/devices/device-attributes" fullPath := tmc.baseURL + path payload := new(bytes.Buffer) if err := json.NewEncoder(payload).Encode(in); err != nil { return nil, errors.WithMessagef(ErrJSONMarshal, failedToParseRequest, err) } payloadStr := payload.String() emap := make(tmtg.EHTSMap).SetAuthorization(tmc.accessToken). SetURI(path). SetHTTPMethod(http.MethodPost). SetContentType(applicationJSON). SetBody(payloadStr) popToken, err := tmc.tg.Generate(emap) if err != nil { return nil, errors.WithMessagef(ErrTokenGen, failedGeneratePod, err) } req, err := http.NewRequestWithContext(ctx, http.MethodPost, fullPath, payload) if err != nil { return nil, err } req.Header.Set(Authorization, tmc.accessToken) req.Header.Set(XAuthOriginator, ToXAuthOriginator(tmc.accessToken)) req.Header.Set(XAuthorization, popToken) req.Header.Set(contentType, applicationJSON) out := &CustomAtributesResponse{} err = tmc.do(req, out) if err != nil { return nil, errors.WithMessage(err, failedToDoRequest) } return out, nil } // Details implements TMobClienter. func (tmc *TMobileMiniClient) Details(ctx context.Context, iccid string) (out *SMSDetailsResponse, err error) { // Really not the way path := fmt.Sprintf("/eitcsr-iotcp-notifications-v2/prd02/iotcp/v2/notifications/sms/messages/%s", iccid) emap := make(tmtg.EHTSMap).SetAuthorization(tmc.accessToken). SetURI(path). SetHTTPMethod(http.MethodGet) token, err := tmc.tg.Generate(emap) if err != nil { return nil, errors.WithMessagef(ErrTokenGen, "failed to generate token: %v", err) } fullPath := tmc.baseURL + path req, err := http.NewRequestWithContext(ctx, http.MethodGet, fullPath, nil) if err != nil { return nil, errors.WithMessagef(ErrCreateRequest, "failed to create request: %v", err) } req.Header.Set(Authorization, tmc.accessToken) req.Header.Set(XAuthOriginator, ToXAuthOriginator(tmc.accessToken)) req.Header.Set(XAuthorization, token) req.Header.Set(contentType, applicationJSON) err = tmc.do(req, &out) if err != nil { return nil, errors.WithMessage(err, failedToDoRequest) } return } // DeviceDetails implements TMobClienter. func (tmc *TMobileMiniClient) DeviceDetails(ctx context.Context, in *DeviceDetailsRequest) (out *DeviceDetailsResponse, err error) { path := "/eitcsr-iotcp-line-of-service-v1/prd02/iotcp/v1/line-of-service/devices/details" fullPath := tmc.baseURL + path payload := new(bytes.Buffer) if err := json.NewEncoder(payload).Encode(in); err != nil { return nil, errors.WithMessagef(ErrJSONMarshal, failedToParseRequest, err) } payloadStr := payload.String() emap := make(tmtg.EHTSMap).SetAuthorization(tmc.accessToken). SetURI(path). SetHTTPMethod(http.MethodPost). SetContentType(applicationJSON). SetBody(payloadStr) popToken, err := tmc.tg.Generate(emap) if err != nil { return nil, errors.WithMessagef(ErrTokenGen, failedGeneratePod, err) } req, err := http.NewRequestWithContext(ctx, http.MethodPost, fullPath, payload) if err != nil { return nil, err } req.Header.Set(Authorization, tmc.accessToken) req.Header.Set(XAuthOriginator, ToXAuthOriginator(tmc.accessToken)) req.Header.Set(XAuthorization, popToken) req.Header.Set(contentType, applicationJSON) out = &DeviceDetailsResponse{} err = tmc.do(req, &out) if err != nil { return nil, errors.WithMessage(err, failedToDoRequest) } return out, nil } // GetProducts implements TMobClienter. func (tmc *TMobileMiniClient) GetProducts(context.Context, *sms.GetAvailableProductsRequest) (*sms.GetAvailableProductsResponse, error) { panic("unimplemented") } // SendSMS implements TMobClienter. func (tmc *TMobileMiniClient) SendSMS(ctx context.Context, in *SendSMSRequest) (out *SendSMSResponse, err error) { panic("unimplemented") } // SetAccessToken implements TMobClienter. // Possible one dumb piece of code func (tmc *TMobileMiniClient) SetAccessToken(accessToken string) { tmc.accessToken = "Bearer " + accessToken } // SetFilter implements TMobClienter. func (tmc *TMobileMiniClient) SetFilter(filter []string) { panic("unimplemented") } var _ TMobClienter = &TMobileMiniClient{} func (c *TMobileMiniClient) do(req *http.Request, out interface{}) error { resp, err := c.client.Do(req) if err != nil { return errors.WithMessagef(ErrDoRequest, "failed to do request: %v", err) } defer resp.Body.Close() if resp.StatusCode >= 300 { var body []byte body, err = io.ReadAll(resp.Body) if err != nil { return errors.WithMessagef(ErrReadResponseBody, "failed to read response body: %v", err) } toWrapErr := ErrBadStatusCode if resp.StatusCode == http.StatusUnauthorized { toWrapErr = ErrAccessTokenExpired } else if resp.StatusCode == http.StatusBadGateway { toWrapErr = ErrBadGatewayCode } else if resp.StatusCode == http.StatusGatewayTimeout { toWrapErr = ErrBadGatewayCode } return errors.WithMessagef(toWrapErr, "request failed with status code %d, body: %s", resp.StatusCode, string(body)) } err = json.NewDecoder(resp.Body).Decode(out) if err != nil { return errors.WithMessagef(ErrJSONMarshal, "failed to decode response: %v", err) } return nil } // sets status as ACTIVATED func (tmc *TMobileMiniClient) handleServiceRestore(ctx context.Context, in *ICCIDBody)(out *ICCIDBody, err error){ path := "/eitcsr-iotcp-line-of-service-v1/prd02/iotcp/v1/line-of-service/devices/service-restore" fullPath := tmc.baseURL + path payload := new(bytes.Buffer) err = json.NewEncoder(payload).Encode(in) if err != nil { return nil, errors.WithMessagef(ErrJSONMarshal, failedToParseRequest, err) } payloadStr := payload.String() fmt.Println(payloadStr) emap := make(tmtg.EHTSMap).SetAuthorization(tmc.accessToken). SetURI(path). SetHTTPMethod(http.MethodPost). SetContentType(applicationJSON). SetBody(payloadStr) popToken, err := tmc.tg.Generate(emap) if err != nil { return nil, errors.WithMessagef(ErrTokenGen, failedGeneratePod, err) } req, err := http.NewRequestWithContext(ctx, http.MethodPost, fullPath, payload) if err != nil { return nil, err } req.Header.Set(Authorization, tmc.accessToken) req.Header.Set(XAuthOriginator, ToXAuthOriginator(tmc.accessToken)) req.Header.Set(XAuthorization, popToken) req.Header.Set(contentType, applicationJSON) out = &ICCIDBody{} err = tmc.do(req, &out) if err != nil { return nil, errors.WithMessage(err, failedToDoRequest) } return } // sets status as DEACTIVATED func (tmc *TMobileMiniClient) handleServiceCancel(ctx context.Context, in *ICCIDBody)(out *ICCIDBody, err error){ path := "/eitcsr-iotcp-line-of-service-v1/prd02/iotcp/v1/line-of-service/devices/service-suspend" fullPath := tmc.baseURL + path payload := new(bytes.Buffer) err = json.NewEncoder(payload).Encode(in) if err != nil { return nil, errors.WithMessagef(ErrJSONMarshal, failedToParseRequest, err) } payloadStr := payload.String() emap := make(tmtg.EHTSMap).SetAuthorization(tmc.accessToken). SetURI(path). SetHTTPMethod(http.MethodPost). SetContentType(applicationJSON). SetBody(payloadStr) popToken, err := tmc.tg.Generate(emap) if err != nil { return nil, errors.WithMessagef(ErrTokenGen, failedGeneratePod, err) } req, err := http.NewRequestWithContext(ctx, http.MethodPost, fullPath, payload) if err != nil { return nil, err } req.Header.Set(Authorization, tmc.accessToken) req.Header.Set(XAuthOriginator, ToXAuthOriginator(tmc.accessToken)) req.Header.Set(XAuthorization, popToken) req.Header.Set(contentType, applicationJSON) out = &ICCIDBody{} err = tmc.do(req, &out) if err != nil { return nil, errors.WithMessage(err, failedToDoRequest) } return }