Skip to content

Commit

Permalink
major:
Browse files Browse the repository at this point in the history
ref: #67, v0.9.0
desc:
- finally got x/oauth2 working
- moved token storage into cwd/.json
- refactored utils.go, removed unused funcs
- edited README, gitignore to reflect changes
  • Loading branch information
samjtro committed Nov 4, 2024
1 parent 3597c25 commit 95fbe3a
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 139 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@
# vendor/

# CUSTOM
*.env
*.env
*.json
*.crt
*.key
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ why should you use this project?
- return structs are easily indexable
- easy to setup, easy to use (personal preference, i know - but trust me!)

---

## docs

### 0.0 quick start
Expand All @@ -31,6 +29,13 @@ SECRET=KEY1 // App Secret
CBURL=https://127.0.0.1 // App Callback URL
```

2. we use tls for the oauth handshake between your local machine & schwab to ensure secure transmission of your bearer token. run the following command to generate ssl certs:

```
openssl req -x509 -out localhost.crt -keyout localhost.key -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' -extensions EXT -config <( \
printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS.1:localhost,IP:127.0.0.1\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")
```

2. `go get github.com/go-schwab/[email protected]`

### 0.1 agent
Expand Down
10 changes: 4 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.23.2

require (
github.com/bytedance/sonic v1.12.2
github.com/go-schwab/oauth2ns v0.0.0-20241027015119-9e1292705f91
github.com/go-schwab/utils/oauth v0.0.0-20241103230919-01b09562dfc2
github.com/joho/godotenv v1.5.1
golang.org/x/oauth2 v0.23.0
)
Expand All @@ -13,14 +13,12 @@ require (
github.com/bytedance/sonic/loader v0.2.0 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/fatih/color v1.17.0 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/nmrshll/rndm-go v0.0.0-20170430161430-8da3024e53de // indirect
github.com/palantir/stacktrace v0.0.0-20161112013806-78658fd2d177 // indirect
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
github.com/stretchr/testify v1.9.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/sys v0.25.0 // indirect
)
18 changes: 6 additions & 12 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQ
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
github.com/go-schwab/oauth2ns v0.0.0-20241027015119-9e1292705f91 h1:2p/Kayfg67+S8/Cip+i/dGcxpkF7q1cwUPyDaVwRZPQ=
github.com/go-schwab/oauth2ns v0.0.0-20241027015119-9e1292705f91/go.mod h1:69d3XNxDSeVmN6g+8a0RT+a13ZxDc5LRKODEZB3d5go=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/go-schwab/utils/oauth v0.0.0-20241103230919-01b09562dfc2 h1:C7kojSdgemr586G27iYSol5L8TUHTR9TWxIwxcBHiag=
github.com/go-schwab/utils/oauth v0.0.0-20241103230919-01b09562dfc2/go.mod h1:8TuoJsd0EHSHnhDFNIQL9MTZKxYt5s0Xv45mGcUnE9U=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
Expand All @@ -26,14 +26,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/nmrshll/rndm-go v0.0.0-20170430161430-8da3024e53de h1:j+mSQhCm1H2d7apFbM5ODqrTultUvF3jt//DcRNkxVM=
github.com/nmrshll/rndm-go v0.0.0-20170430161430-8da3024e53de/go.mod h1:OeEnWnbCrUWnPl1xSCGM5/qtWqZ4L15KOAjR/wmxhXc=
github.com/palantir/stacktrace v0.0.0-20161112013806-78658fd2d177 h1:nRlQD0u1871kaznCnn1EvYiMbum36v7hw1DLPEjds4o=
github.com/palantir/stacktrace v0.0.0-20161112013806-78658fd2d177/go.mod h1:ao5zGxj8Z4x60IOVYZUbDSmt3R8Ddo080vEgPosHpak=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
Expand All @@ -51,8 +45,8 @@ golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
Expand Down
159 changes: 41 additions & 118 deletions utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package trader

import (
"context"
"crypto/tls"
"errors"
"fmt"
"io"
"io/fs"
"log"
Expand All @@ -13,18 +13,25 @@ import (
"strings"

"github.com/bytedance/sonic"
o "github.com/go-schwab/oauth2ns"
o "github.com/go-schwab/utils/oauth"
"github.com/joho/godotenv"
"golang.org/x/oauth2"
)

type Agent struct{ client *o.AuthorizedClient }
type Agent struct{ *o.AuthorizedClient }

var TOKENS oauth2.Token
var (
APPKEY string
SECRET string
CBURL string
)

func init() {
err := godotenv.Load(findAllEnvFiles()...)
isErrNil(err)
APPKEY = os.Getenv("APPKEY")
SECRET = os.Getenv("SECRET")
CBURL = os.Getenv("CBURL")
}

// is the err nil?
Expand Down Expand Up @@ -53,119 +60,45 @@ func findAllEnvFiles() []string {
return files
}

// wrapper for os.UserHomeDir()
func homeDir() string {
dir, err := os.UserHomeDir()
isErrNil(err)
return dir
}

// Credit: https://go.dev/play/p/C2sZRYC15XN
func getStringInBetween(str string, start string, end string) (result string) {
s := strings.Index(str, start)
if s == -1 {
return
}
s += len(start)
e := strings.Index(str[s:], end)
if e == -1 {
return
}
return str[s : s+e]
}

// trim one FIRST character in the string
func trimOneFirst(s string) string {
if len(s) < 1 {
return ""
}
return s[1:]
}

// trim one LAST character in the string
func trimOneLast(s string) string {
if len(s) < 1 {
return ""
}
return s[:len(s)-1]
}

// trim one FIRST & one LAST character in the string
func trimOneFirstOneLast(s string) string {
if len(s) < 1 {
return ""
}
return s[1 : len(s)-1]
}

// trim two FIRST & one LAST character in the string
func trimTwoFirstOneLast(s string) string {
if len(s) < 1 {
return ""
}
return s[2 : len(s)-1]
}

// trim one FIRST & two LAST character in the string
func trimOneFirstTwoLast(s string) string {
if len(s) < 1 {
return ""
}
return s[1 : len(s)-2]
}

// trim one FIRST & three LAST character in the string
func trimOneFirstThreeLast(s string) string {
if len(s) < 1 {
return ""
}
return s[1 : len(s)-3]
}

// Read in tokens from ~/.trade/bar.json
func readDB() *o.AuthorizedClient {
body, err := os.ReadFile(fmt.Sprintf("%s/.trade/bar.json", homeDir()))
// Read in tokens from .json
func readDB() Agent {
var tok *oauth2.Token
body, err := os.ReadFile(".json")
isErrNil(err)
var ctx context.Context
err = sonic.Unmarshal(body, &TOKENS)
err = sonic.Unmarshal(body, &tok)
isErrNil(err)
c := &oauth2.Config{
ClientID: os.Getenv("APPKEY"),
ClientSecret: os.Getenv("SECRET"),
conf := &oauth2.Config{
ClientID: APPKEY, // Schwab App Key
ClientSecret: SECRET, // Schwab App Secret
Endpoint: oauth2.Endpoint{
AuthURL: "https://api.schwabapi.com/v1/oauth/authorize",
TokenURL: "https://api.schwabapi.com/v1/oauth/token",
},
}
return &o.AuthorizedClient{
c.Client(ctx, &TOKENS),
&TOKENS,
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
sslcli := &http.Client{Transport: tr}
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, sslcli)
return Agent{
&o.AuthorizedClient{
conf.Client(ctx, tok),
tok,
},
}
}

func Initiate() *Agent {
var agent Agent
// TODO: test this block, this is to attempt to resolve the error described in #67
if _, err := os.Stat(fmt.Sprintf("%s/.trade/bar.json", homeDir())); errors.Is(err, os.ErrNotExist) {
if _, err := os.Stat(fmt.Sprintf("%s/.trade", homeDir())); !errors.Is(err, os.ErrNotExist) {
err = os.RemoveAll(fmt.Sprintf("%s/.trade", homeDir()))
isErrNil(err)
}
err = os.Mkdir(fmt.Sprintf("%s/.trade", homeDir()), os.ModePerm)
isErrNil(err)
agent.client, err = o.Initiate(os.Getenv("APPKEY"), os.Getenv("SECRET"), os.Getenv("CBURL"))
if _, err := os.Stat(".json"); errors.Is(err, os.ErrNotExist) {
agent = Agent{o.Initiate(APPKEY, SECRET)}
bytes, err := sonic.Marshal(agent.Token)
isErrNil(err)
TOKENS = *agent.client.Token
bytes, err := sonic.Marshal(TOKENS)
err = os.WriteFile(fmt.Sprintf("%s/.trade/bar.json", homeDir()), bytes, 0777)
err = os.WriteFile(".json", bytes, 0777)
isErrNil(err)
} else {
agent.client = readDB()
if TOKENS.AccessToken == "" {
err := os.RemoveAll(fmt.Sprintf("%s/.trade", homeDir()))
isErrNil(err)
log.Fatalf("[err] something went wrong - please reinitiate with 'Initiate'")
}
agent = readDB()
}
return &agent
}
Expand All @@ -177,32 +110,22 @@ func Initiate() *Agent {
// req = a request of type *http.Request
func (agent *Agent) Handler(req *http.Request) (*http.Response, error) {
var err error
if TOKENS.AccessToken == "" {
log.Fatalf("[err] no access token found, please reinitiate with 'Initiate'")
}
if ((&Agent{}) == agent) || ((&o.AuthorizedClient{}) == agent.client) {
// TODO: this theoretically works but results in an error for the oauth implementation
// going to do some testing now, but pushing as it is in what is a theoretically working state
agent.client, err = o.Initiate(os.Getenv("APPKEY"), os.Getenv("SECRET"), os.Getenv("CBURL"))
isErrNil(err)
TOKENS = *agent.client.Token
if agent.Token.AccessToken == "" {
log.Fatal("[fatal] no access token found, please reinitiate with 'Initiate'")
// TODO: auto reinitiate?
}
resp, err := agent.client.Do(req)
resp, err := agent.Do(req)
if err != nil {
return resp, err
}
// TODO: test this block
var statusErr error
switch true {
case resp.StatusCode == 401:
err := os.RemoveAll(fmt.Sprintf("%s/.trade", homeDir()))
isErrNil(err)
statusErr = errors.New("[err] invalid token - please reinitiate with 'Initiate'")
log.Fatal("[fatal] invalid token - please reinitiate with 'Initiate'")
case resp.StatusCode < 200, resp.StatusCode > 300:
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
isErrNil(err)
statusErr = errors.New(fmt.Sprintf("[err] %d - %s", resp.StatusCode, body))
log.Println("[err] ", string(body))
}
return resp, statusErr
return resp, nil
}

0 comments on commit 95fbe3a

Please sign in to comment.