tableserviceclient.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. package storage
  2. // Copyright 2017 Microsoft Corporation
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. import (
  16. "encoding/json"
  17. "fmt"
  18. "io/ioutil"
  19. "net/http"
  20. "net/url"
  21. "strconv"
  22. )
  23. const (
  24. headerAccept = "Accept"
  25. headerEtag = "Etag"
  26. headerPrefer = "Prefer"
  27. headerXmsContinuation = "x-ms-Continuation-NextTableName"
  28. )
  29. // TableServiceClient contains operations for Microsoft Azure Table Storage
  30. // Service.
  31. type TableServiceClient struct {
  32. client Client
  33. auth authentication
  34. }
  35. // TableOptions includes options for some table operations
  36. type TableOptions struct {
  37. RequestID string
  38. }
  39. func (options *TableOptions) addToHeaders(h map[string]string) map[string]string {
  40. if options != nil {
  41. h = addToHeaders(h, "x-ms-client-request-id", options.RequestID)
  42. }
  43. return h
  44. }
  45. // QueryNextLink includes information for getting the next page of
  46. // results in query operations
  47. type QueryNextLink struct {
  48. NextLink *string
  49. ml MetadataLevel
  50. }
  51. // GetServiceProperties gets the properties of your storage account's table service.
  52. // See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-table-service-properties
  53. func (t *TableServiceClient) GetServiceProperties() (*ServiceProperties, error) {
  54. return t.client.getServiceProperties(tableServiceName, t.auth)
  55. }
  56. // SetServiceProperties sets the properties of your storage account's table service.
  57. // See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/set-table-service-properties
  58. func (t *TableServiceClient) SetServiceProperties(props ServiceProperties) error {
  59. return t.client.setServiceProperties(props, tableServiceName, t.auth)
  60. }
  61. // GetTableReference returns a Table object for the specified table name.
  62. func (t *TableServiceClient) GetTableReference(name string) *Table {
  63. return &Table{
  64. tsc: t,
  65. Name: name,
  66. }
  67. }
  68. // QueryTablesOptions includes options for some table operations
  69. type QueryTablesOptions struct {
  70. Top uint
  71. Filter string
  72. RequestID string
  73. }
  74. func (options *QueryTablesOptions) getParameters() (url.Values, map[string]string) {
  75. query := url.Values{}
  76. headers := map[string]string{}
  77. if options != nil {
  78. if options.Top > 0 {
  79. query.Add(OdataTop, strconv.FormatUint(uint64(options.Top), 10))
  80. }
  81. if options.Filter != "" {
  82. query.Add(OdataFilter, options.Filter)
  83. }
  84. headers = addToHeaders(headers, "x-ms-client-request-id", options.RequestID)
  85. }
  86. return query, headers
  87. }
  88. // QueryTables returns the tables in the storage account.
  89. // You can use query options defined by the OData Protocol specification.
  90. //
  91. // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/query-tables
  92. func (t *TableServiceClient) QueryTables(ml MetadataLevel, options *QueryTablesOptions) (*TableQueryResult, error) {
  93. query, headers := options.getParameters()
  94. uri := t.client.getEndpoint(tableServiceName, tablesURIPath, query)
  95. return t.queryTables(uri, headers, ml)
  96. }
  97. // NextResults returns the next page of results
  98. // from a QueryTables or a NextResults operation.
  99. //
  100. // See https://docs.microsoft.com/rest/api/storageservices/fileservices/query-tables
  101. // See https://docs.microsoft.com/rest/api/storageservices/fileservices/query-timeout-and-pagination
  102. func (tqr *TableQueryResult) NextResults(options *TableOptions) (*TableQueryResult, error) {
  103. if tqr == nil {
  104. return nil, errNilPreviousResult
  105. }
  106. if tqr.NextLink == nil {
  107. return nil, errNilNextLink
  108. }
  109. headers := options.addToHeaders(map[string]string{})
  110. return tqr.tsc.queryTables(*tqr.NextLink, headers, tqr.ml)
  111. }
  112. // TableQueryResult contains the response from
  113. // QueryTables and QueryTablesNextResults functions.
  114. type TableQueryResult struct {
  115. OdataMetadata string `json:"odata.metadata"`
  116. Tables []Table `json:"value"`
  117. QueryNextLink
  118. tsc *TableServiceClient
  119. }
  120. func (t *TableServiceClient) queryTables(uri string, headers map[string]string, ml MetadataLevel) (*TableQueryResult, error) {
  121. if ml == EmptyPayload {
  122. return nil, errEmptyPayload
  123. }
  124. headers = mergeHeaders(headers, t.client.getStandardHeaders())
  125. headers[headerAccept] = string(ml)
  126. resp, err := t.client.exec(http.MethodGet, uri, headers, nil, t.auth)
  127. if err != nil {
  128. return nil, err
  129. }
  130. defer resp.Body.Close()
  131. if err := checkRespCode(resp, []int{http.StatusOK}); err != nil {
  132. return nil, err
  133. }
  134. respBody, err := ioutil.ReadAll(resp.Body)
  135. if err != nil {
  136. return nil, err
  137. }
  138. var out TableQueryResult
  139. err = json.Unmarshal(respBody, &out)
  140. if err != nil {
  141. return nil, err
  142. }
  143. for i := range out.Tables {
  144. out.Tables[i].tsc = t
  145. }
  146. out.tsc = t
  147. nextLink := resp.Header.Get(http.CanonicalHeaderKey(headerXmsContinuation))
  148. if nextLink == "" {
  149. out.NextLink = nil
  150. } else {
  151. originalURI, err := url.Parse(uri)
  152. if err != nil {
  153. return nil, err
  154. }
  155. v := originalURI.Query()
  156. v.Set(nextTableQueryParameter, nextLink)
  157. newURI := t.client.getEndpoint(tableServiceName, tablesURIPath, v)
  158. out.NextLink = &newURI
  159. out.ml = ml
  160. }
  161. return &out, nil
  162. }
  163. func addBodyRelatedHeaders(h map[string]string, length int) map[string]string {
  164. h[headerContentType] = "application/json"
  165. h[headerContentLength] = fmt.Sprintf("%v", length)
  166. h[headerAcceptCharset] = "UTF-8"
  167. return h
  168. }
  169. func addReturnContentHeaders(h map[string]string, ml MetadataLevel) map[string]string {
  170. if ml != EmptyPayload {
  171. h[headerPrefer] = "return-content"
  172. h[headerAccept] = string(ml)
  173. } else {
  174. h[headerPrefer] = "return-no-content"
  175. // From API version 2015-12-11 onwards, Accept header is required
  176. h[headerAccept] = string(NoMetadata)
  177. }
  178. return h
  179. }