@@ -17,6 +17,7 @@ package ecobee
1717import (
1818 "context"
1919 "encoding/json"
20+ "errors"
2021 "fmt"
2122 "io/ioutil"
2223 "net/http"
@@ -31,17 +32,45 @@ import (
3132var Scopes = []string {"smartRead" , "smartWrite" }
3233
3334type tokenSource struct {
34- token oauth2.Token
35- cacheFile , clientID string
35+ token oauth2.Token
36+ clientID string
37+ authCache AuthCache
38+ }
39+
40+ // AuthCache encapsulates persistent cache for OAuth2 token.
41+ type AuthCache interface {
42+ LoadTokenData () ([]byte , error )
43+ SaveTokenData (data []byte ) error
44+ }
45+
46+ type fileAuthCache struct {
47+ fileName string
48+ }
49+
50+ func (p fileAuthCache ) LoadTokenData () ([]byte , error ) {
51+ return ioutil .ReadFile (p .fileName )
52+ }
53+
54+ func (p fileAuthCache ) SaveTokenData (data []byte ) error {
55+ return ioutil .WriteFile (p .fileName , data , 0777 )
56+ }
57+
58+ // TokenCacheFile returns AuthCache that persists the OAuth2 token in the specified file.
59+ func TokenCacheFile (fileName string ) AuthCache {
60+ return fileAuthCache {fileName }
3661}
3762
3863func TokenSource (clientID , cacheFile string ) oauth2.TokenSource {
39- return oauth2 . ReuseTokenSource ( nil , newTokenSource ( clientID , cacheFile ) )
64+ return TokenSourceWithAuthCache ( clientID , fileAuthCache { cacheFile } )
4065}
4166
42- func newTokenSource (clientID , cacheFile string ) * tokenSource {
43- file , err := ioutil .ReadFile (cacheFile )
44- ets := tokenSource {clientID : clientID , cacheFile : cacheFile }
67+ func TokenSourceWithAuthCache (clientID string , authCache AuthCache ) oauth2.TokenSource {
68+ return oauth2 .ReuseTokenSource (nil , newTokenSource (clientID , authCache ))
69+ }
70+
71+ func newTokenSource (clientID string , authCache AuthCache ) * tokenSource {
72+ file , err := authCache .LoadTokenData ()
73+ ets := tokenSource {clientID : clientID , authCache : authCache }
4574 if err != nil {
4675 // no file, corrupted, or other problem: just start with an
4776 // empty token.
@@ -56,8 +85,7 @@ func (ts *tokenSource) save() error {
5685 if err != nil {
5786 return err
5887 }
59- err = ioutil .WriteFile (ts .cacheFile , d , 0777 )
60- return err
88+ return ts .authCache .SaveTokenData (d )
6189}
6290
6391func (ts * tokenSource ) firstAuth () error {
@@ -204,8 +232,38 @@ type Client struct {
204232// (Application Key). Use the Ecobee Developer Portal to create the
205233// Application Key.
206234// (https://www.ecobee.com/consumerportal/index.html#/dev)
235+ // This function is deprecated - use New() instead.
207236func NewClient (clientID , authCache string ) * Client {
208- return & Client {oauth2 .NewClient (
209- context .Background (),
210- TokenSource (clientID , authCache ))}
237+ c , err := New (context .TODO (), clientID , Options {
238+ AuthCache : fileAuthCache {authCache },
239+ })
240+
241+ if err != nil {
242+ // can't fail here.
243+ panic ("unexpected usage: " + err .Error ())
244+ }
245+
246+ return c
247+ }
248+
249+ // Options specifies EcoBee client parameters.
250+ type Options struct {
251+ ApplicationID string // application ID created in Ecobee Developer Portal
252+ AuthCache AuthCache // token cache, typically TokenCacheFile(fileName)
253+ }
254+
255+ // New creates a Ecobee API client for the specific options
256+ // Use the Ecobee Developer Portal to create the Application Key.
257+ // (https://www.ecobee.com/consumerportal/index.html#/dev)
258+ func New (ctx context.Context , clientID string , opt Options ) (* Client , error ) {
259+ if opt .ApplicationID == "" {
260+ return nil , errors .New ("application ID is required" )
261+ }
262+ if opt .AuthCache == nil {
263+ return nil , fmt .Errorf ("auth cache is required" )
264+ }
265+
266+ ts := TokenSourceWithAuthCache (clientID , opt .AuthCache )
267+ cli := oauth2 .NewClient (ctx , ts )
268+ return & Client {cli }, nil
211269}
0 commit comments