Files
cloud-services/pkg/tmobile/client_mini.go

368 lines
10 KiB
Go

package tmobile
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
errorsO "errors"
"github.com/fiskerinc/cloud-services/pkg/grpc/sms"
tmtg "github.com/fiskerinc/cloud-services/pkg/tmobtokengen"
"github.com/fiskerinc/cloud-services/pkg/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
}