sdk.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. // Copyright (c) Dropbox, Inc.
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy
  4. // of this software and associated documentation files (the "Software"), to deal
  5. // in the Software without restriction, including without limitation the rights
  6. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. // copies of the Software, and to permit persons to whom the Software is
  8. // furnished to do so, subject to the following conditions:
  9. //
  10. // The above copyright notice and this permission notice shall be included in
  11. // all copies or substantial portions of the Software.
  12. //
  13. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. // THE SOFTWARE.
  20. package dropbox
  21. import (
  22. "fmt"
  23. "io"
  24. "net/http"
  25. "golang.org/x/oauth2"
  26. )
  27. const (
  28. apiVersion = 2
  29. defaultDomain = ".dropboxapi.com"
  30. hostAPI = "api"
  31. hostContent = "content"
  32. hostNotify = "notify"
  33. sdkVersion = "UNKNOWN SDK VERSION"
  34. specVersion = "UNKNOWN SPEC VERSION"
  35. )
  36. // Version returns the current SDK version and API Spec version
  37. func Version() (string, string) {
  38. return sdkVersion, specVersion
  39. }
  40. // Config contains parameters for configuring the SDK.
  41. type Config struct {
  42. // OAuth2 access token
  43. Token string
  44. // Enable verbose logging in SDK
  45. Verbose bool
  46. // Used with APIs that support operations as another user
  47. AsMemberID string
  48. // No need to set -- for testing only
  49. Domain string
  50. // No need to set -- for testing only
  51. Client *http.Client
  52. // No need to set -- for testing only
  53. HeaderGenerator func(hostType string, style string, namespace string, route string) map[string]string
  54. // No need to set -- for testing only
  55. URLGenerator func(hostType string, style string, namespace string, route string) string
  56. }
  57. // Context is the base client context used to implement per-namespace clients.
  58. type Context struct {
  59. Config Config
  60. Client *http.Client
  61. HeaderGenerator func(hostType string, style string, namespace string, route string) map[string]string
  62. URLGenerator func(hostType string, style string, namespace string, route string) string
  63. }
  64. // NewRequest returns an appropriate Request object for the given namespace/route.
  65. func (c *Context) NewRequest(
  66. hostType string,
  67. style string,
  68. authed bool,
  69. namespace string,
  70. route string,
  71. headers map[string]string,
  72. body io.Reader,
  73. ) (*http.Request, error) {
  74. url := c.URLGenerator(hostType, style, namespace, route)
  75. req, err := http.NewRequest("POST", url, body)
  76. if err != nil {
  77. return nil, err
  78. }
  79. for k, v := range headers {
  80. req.Header.Add(k, v)
  81. }
  82. for k, v := range c.HeaderGenerator(hostType, style, namespace, route) {
  83. req.Header.Add(k, v)
  84. }
  85. if req.Header.Get("Host") != "" {
  86. req.Host = req.Header.Get("Host")
  87. }
  88. if !authed {
  89. req.Header.Del("Authorization")
  90. }
  91. return req, nil
  92. }
  93. // NewContext returns a new Context with the given Config.
  94. func NewContext(c Config) Context {
  95. domain := c.Domain
  96. if domain == "" {
  97. domain = defaultDomain
  98. }
  99. client := c.Client
  100. if client == nil {
  101. var conf = &oauth2.Config{Endpoint: OAuthEndpoint(domain)}
  102. tok := &oauth2.Token{AccessToken: c.Token}
  103. client = conf.Client(oauth2.NoContext, tok)
  104. }
  105. headerGenerator := c.HeaderGenerator
  106. if headerGenerator == nil {
  107. headerGenerator = func(hostType string, style string, namespace string, route string) map[string]string {
  108. return map[string]string{}
  109. }
  110. }
  111. urlGenerator := c.URLGenerator
  112. if urlGenerator == nil {
  113. hostMap := map[string]string{
  114. hostAPI: hostAPI + domain,
  115. hostContent: hostContent + domain,
  116. hostNotify: hostNotify + domain,
  117. }
  118. urlGenerator = func(hostType string, style string, namespace string, route string) string {
  119. fqHost := hostMap[hostType]
  120. return fmt.Sprintf("https://%s/%d/%s/%s", fqHost, apiVersion, namespace, route)
  121. }
  122. }
  123. return Context{c, client, headerGenerator, urlGenerator}
  124. }
  125. // OAuthEndpoint constructs an `oauth2.Endpoint` for the given domain
  126. func OAuthEndpoint(domain string) oauth2.Endpoint {
  127. if domain == "" {
  128. domain = defaultDomain
  129. }
  130. authURL := fmt.Sprintf("https://meta%s/1/oauth2/authorize", domain)
  131. tokenURL := fmt.Sprintf("https://api%s/1/oauth2/token", domain)
  132. if domain == defaultDomain {
  133. authURL = "https://www.dropbox.com/1/oauth2/authorize"
  134. }
  135. return oauth2.Endpoint{AuthURL: authURL, TokenURL: tokenURL}
  136. }
  137. // Tagged is used for tagged unions.
  138. type Tagged struct {
  139. Tag string `json:".tag"`
  140. }
  141. // APIError is the base type for endpoint-specific errors.
  142. type APIError struct {
  143. ErrorSummary string `json:"error_summary"`
  144. }
  145. func (e APIError) Error() string {
  146. return e.ErrorSummary
  147. }
  148. func init() {
  149. // These are not registered in the oauth library by default
  150. oauth2.RegisterBrokenAuthHeaderProvider("https://api.dropboxapi.com")
  151. oauth2.RegisterBrokenAuthHeaderProvider("https://api-dbdev.dev.corp.dropbox.com")
  152. }