Skip to content
This repository has been archived by the owner on Jul 19, 2023. It is now read-only.

Commit

Permalink
Early prototype code.
Browse files Browse the repository at this point in the history
  • Loading branch information
judy2k committed Apr 23, 2017
1 parent a81be15 commit 3313a99
Show file tree
Hide file tree
Showing 3 changed files with 240 additions and 0 deletions.
125 changes: 125 additions & 0 deletions auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package nexmo

import (
"crypto/rsa"
"math/rand"
"strconv"
"time"

"fmt"

"github.com/dghubble/sling"
"github.com/dgrijalva/jwt-go"
)

type AuthType uint8

type RandomProvider interface {
Int31() int32
}

const (
ApiSecretAuth AuthType = iota + 1
ApiSecretPathAuth
JwtAuth
)

type AuthSet struct {
apiSecret *apiSecretAuth
appAuth *applicationAuth
}

func NewAuthSet() *AuthSet {
return new(AuthSet)
}

func (a *AuthSet) SetAPISecret(apiKey, apiSecret string) {
a.apiSecret = &apiSecretAuth{
apiKey: apiKey,
apiSecret: apiSecret,
}
}

func (a *AuthSet) GenerateToken() (string, error) {
if a.appAuth == nil {
return "", fmt.Errorf("must call SetApplicationAuth before calling GenerateToken")
}
return a.appAuth.generateToken()
}

func (a *AuthSet) SetApplicationAuth(appID string, key []byte) error {
privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(key)
if err != nil {
return err
}
a.appAuth = &applicationAuth{
appID: appID,
privateKey: privateKey,
r: rand.New(rand.NewSource(time.Now().UnixNano())),
}
return nil
}

func (a *AuthSet) ApplyAuth(acceptableAuths []AuthType, sling *sling.Sling, request apiSecretRequest) error {
for _, acceptableAuth := range acceptableAuths {
switch acceptableAuth {
case ApiSecretAuth:
if a.apiSecret != nil {
return a.apiSecret.apply(request)
}
case ApiSecretPathAuth:
if a.apiSecret != nil {
sling.Path(fmt.Sprintf("%s/%s", a.apiSecret.apiKey, a.apiSecret.apiSecret))
}
case JwtAuth:
token, err := a.appAuth.generateToken()
if err != nil {
return err
}
if a.appAuth != nil {
sling.Set("Authorization", fmt.Sprintf("Bearer %v", token))
}
}
}
return fmt.Errorf("AuthSet not configured with credentials for %x", acceptableAuths)
}

type apiSecretRequest interface {
applyAPISecret(apiKey, apiSecret string)
}

type apiSecretAuth struct {
apiKey string
apiSecret string
}

func (a apiSecretAuth) apply(request apiSecretRequest) error {
if request == nil {
return fmt.Errorf("cannot apply api_key and api_secret to a nil request")
}
request.applyAPISecret(a.apiKey, a.apiSecret)
return nil
}

type applicationAuth struct {
appID string
privateKey *rsa.PrivateKey
r RandomProvider
}

type jwtClaims struct {
ApplicationID string `json:"application_id"`
jwt.StandardClaims
}

func (a applicationAuth) generateToken() (string, error) {
claims := jwtClaims{
a.appID,
jwt.StandardClaims{
Id: strconv.Itoa(int(a.r.Int31())),
IssuedAt: time.Now().UTC().Unix(),
},
}
token := jwt.NewWithClaims(jwt.GetSigningMethod("RS256"), claims)
return token.SignedString(a.privateKey)
}
81 changes: 81 additions & 0 deletions insight.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package nexmo

import (
"net/http"

"fmt"

"github.com/dghubble/sling"
)

type InsightService struct {
sling *sling.Sling
authSet *AuthSet
}

type BasicInsightRequest struct {
Number string
Country string
Cnam *bool
}

type basicInsightJSONRequest struct {
APIKey string `json:"api_key"`
APISecret string `json:"api_secret"`
Number string `json:"number,omitempty"`
Country string `json:"country,omitempty"`
}

type BasicInsightResponse struct {
Status int64 `json:"status"`
StatusMessage string `json:"status_message,omitempty"`
ErrorText string `json:"error_text,omitempty"`
InternationalFormatNumber string `json:"international_format_number,omitempty"`
NationalFormatNumber string `json:"national_format_number,omitempty"`
CountryCode string `json:"country_code,omitempty"`
CountryCodeIso3 string `json:"country_code_iso3,omitempty"`
CountryName string `json:"country_name,omitempty"`
CountryPrefix string `json:"country_prefix,omitempty"`
}

func (r *BasicInsightResponse) responseError() error {
if r.Status != 0 {
if r.StatusMessage != "" {
return fmt.Errorf("%d: %s", r.Status, r.StatusMessage)
}
return fmt.Errorf("%d: %s", r.Status, r.ErrorText)
}
return nil
}

func newInsightService(base *sling.Sling, authSet *AuthSet) *InsightService {
sling := base.Base("https://api.nexmo.com/ni/")
return &InsightService{
sling: sling,
authSet: authSet,
}
}

func (c *InsightService) GetBasicInsight(request BasicInsightRequest) (BasicInsightResponse, *http.Response, error) {
if c.authSet.apiSecret == nil {
return BasicInsightResponse{}, nil, fmt.Errorf("Cannot call GetBasicInsight without providing APISecretAuth")
}
jsonRequest := basicInsightJSONRequest{
APIKey: c.authSet.apiSecret.apiKey,
APISecret: c.authSet.apiSecret.apiSecret,
Number: request.Number,
Country: request.Country,
}

insightResponse := new(BasicInsightResponse)
resp, err := c.sling.New().Post("basic/json").BodyJSON(jsonRequest).ReceiveSuccess(insightResponse)
if err != nil {
return *insightResponse, resp, err
}
err = insightResponse.responseError()
return *insightResponse, resp, err
}

func (c *InsightService) SetBaseURL(baseURL string) {
c.sling.Base(baseURL)
}
34 changes: 34 additions & 0 deletions nexmo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package nexmo

import (
"fmt"
"net/http"

"github.com/dghubble/sling"
)

type Client struct {
sling *sling.Sling
Insight *InsightService
//SMS *SMSService
}

func NewClient(httpClient *http.Client, authSet *AuthSet) *Client {
base := sling.New().
Client(httpClient).
Set("User-Agent", "nexmo-go/2.0 (judy2k)")
return &Client{
sling: base,
Insight: newInsightService(base.New(), authSet),
//SMS: newSMSService(base.New(), authSet),
}
}

type APIError struct {
Status int64
ErrorMessage string
}

func (a APIError) Error() string {
return fmt.Sprintf("%d: %s", a.Status, a.ErrorMessage)
}

0 comments on commit 3313a99

Please sign in to comment.