go_client.stoneg.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. import os
  2. from stone.generator import CodeGenerator
  3. from stone.data_type import (
  4. is_void_type,
  5. is_struct_type
  6. )
  7. from go_helpers import (
  8. HEADER,
  9. fmt_type,
  10. fmt_var,
  11. generate_doc,
  12. )
  13. class GoClientGenerator(CodeGenerator):
  14. def generate(self, api):
  15. for namespace in api.namespaces.values():
  16. if len(namespace.routes) > 0:
  17. self._generate_client(namespace)
  18. def _generate_client(self, namespace):
  19. file_name = os.path.join(self.target_folder_path, namespace.name,
  20. 'client.go')
  21. with self.output_to_relative_path(file_name):
  22. self.emit_raw(HEADER)
  23. self.emit()
  24. self.emit('package %s' % namespace.name)
  25. self.emit()
  26. self.emit('// Client interface describes all routes in this namespace')
  27. with self.block('type Client interface'):
  28. for route in namespace.routes:
  29. generate_doc(self, route)
  30. self.emit(self._generate_route_signature(namespace, route))
  31. self.emit()
  32. self.emit('type apiImpl dropbox.Context')
  33. for route in namespace.routes:
  34. self._generate_route(namespace, route)
  35. self.emit('// New returns a Client implementation for this namespace')
  36. with self.block('func New(c dropbox.Config) *apiImpl'):
  37. self.emit('ctx := apiImpl(dropbox.NewContext(c))')
  38. self.emit('return &ctx')
  39. def _generate_route_signature(self, namespace, route):
  40. req = fmt_type(route.arg_data_type, namespace)
  41. res = fmt_type(route.result_data_type, namespace, use_interface=True)
  42. fn = fmt_var(route.name)
  43. style = route.attrs.get('style', 'rpc')
  44. arg = '' if is_void_type(route.arg_data_type) else 'arg {req}'
  45. ret = '(err error)' if is_void_type(route.result_data_type) else \
  46. '(res {res}, err error)'
  47. signature = '{fn}(' + arg + ') ' + ret
  48. if style == 'download':
  49. signature = '{fn}(' + arg + \
  50. ') (res {res}, content io.ReadCloser, err error)'
  51. elif style == 'upload':
  52. signature = '{fn}(' + arg + ', content io.Reader) ' + ret
  53. if is_void_type(route.arg_data_type):
  54. signature = '{fn}(content io.Reader) ' + ret
  55. return signature.format(fn=fn, req=req, res=res)
  56. def _generate_route(self, namespace, route):
  57. out = self.emit
  58. fn = fmt_var(route.name)
  59. err = fmt_type(route.error_data_type, namespace)
  60. out('//%sAPIError is an error-wrapper for the %s route' %
  61. (fn, route.name))
  62. with self.block('type {fn}APIError struct'.format(fn=fn)):
  63. out('dropbox.APIError')
  64. out('EndpointError {err} `json:"error"`'.format(err=err))
  65. out()
  66. signature = 'func (dbx *apiImpl) ' + self._generate_route_signature(
  67. namespace, route)
  68. with self.block(signature):
  69. out('cli := dbx.Client')
  70. out()
  71. self._generate_request(namespace, route)
  72. self._generate_post()
  73. self._generate_response(route)
  74. with self.block('if resp.StatusCode == http.StatusOK'):
  75. self._generate_result(route)
  76. self._generate_error_handling(route)
  77. out()
  78. def _generate_request(self, namespace, route):
  79. out = self.emit
  80. auth = route.attrs.get('auth', '')
  81. host = route.attrs.get('host', 'api')
  82. style = route.attrs.get('style', 'rpc')
  83. body = 'nil'
  84. if not is_void_type(route.arg_data_type):
  85. with self.block('if dbx.Config.Verbose'):
  86. out('log.Printf("arg: %v", arg)')
  87. out('b, err := json.Marshal(arg)')
  88. with self.block('if err != nil'):
  89. out('return')
  90. out()
  91. if host != 'content':
  92. body = 'bytes.NewReader(b)'
  93. if style == 'upload':
  94. body = 'content'
  95. headers = {}
  96. if not is_void_type(route.arg_data_type):
  97. if host == 'content':
  98. headers["Dropbox-API-Arg"] = "string(b)"
  99. else:
  100. headers["Content-Type"] = '"application/json"'
  101. if style == 'upload':
  102. headers["Content-Type"] = '"application/octet-stream"'
  103. out('headers := map[string]string{')
  104. for k, v in headers.items():
  105. out('\t"{}": {},'.format(k, v))
  106. out('}')
  107. if auth != 'noauth' and auth != 'team':
  108. with self.block('if dbx.Config.AsMemberID != ""'):
  109. out('headers["Dropbox-API-Select-User"] = dbx.Config.AsMemberID')
  110. out()
  111. authed = 'false' if auth == 'noauth' else 'true'
  112. out('req, err := (*dropbox.Context)(dbx).NewRequest("{}", "{}", {}, "{}", "{}", headers, {})'.format(
  113. host, style, authed, namespace.name, route.name, body))
  114. with self.block('if err != nil'):
  115. out('return')
  116. with self.block('if dbx.Config.Verbose'):
  117. out('log.Printf("req: %v", req)')
  118. out()
  119. def _generate_post(self):
  120. out = self.emit
  121. out('resp, err := cli.Do(req)')
  122. with self.block('if dbx.Config.Verbose'):
  123. out('log.Printf("resp: %v", resp)')
  124. with self.block('if err != nil'):
  125. out('return')
  126. out()
  127. def _generate_response(self, route):
  128. out = self.emit
  129. style = route.attrs.get('style', 'rpc')
  130. if style == 'download':
  131. out('body := []byte(resp.Header.Get("Dropbox-API-Result"))')
  132. out('content = resp.Body')
  133. else:
  134. out('defer resp.Body.Close()')
  135. with self.block('body, err := ioutil.ReadAll(resp.Body);'
  136. 'if err != nil'):
  137. out('return')
  138. out()
  139. with self.block('if dbx.Config.Verbose'):
  140. out('log.Printf("body: %s", body)')
  141. def _generate_error_handling(self, route):
  142. out = self.emit
  143. with self.block('if resp.StatusCode == http.StatusConflict'):
  144. out('var apiError %sAPIError' % fmt_var(route.name))
  145. with self.block('err = json.Unmarshal(body, &apiError);'
  146. 'if err != nil'):
  147. out('return')
  148. out('err = apiError')
  149. out('return')
  150. out('var apiError dropbox.APIError')
  151. with self.block('if resp.StatusCode == http.StatusBadRequest'):
  152. out('apiError.ErrorSummary = string(body)')
  153. out('err = apiError')
  154. out('return')
  155. with self.block('err = json.Unmarshal(body, &apiError);'
  156. 'if err != nil'):
  157. out('return')
  158. out('err = apiError')
  159. out('return')
  160. def _generate_result(self, route):
  161. out = self.emit
  162. if is_struct_type(route.result_data_type) and \
  163. route.result_data_type.has_enumerated_subtypes():
  164. out('var tmp %sUnion' % fmt_var(route.result_data_type.name, export=False))
  165. with self.block('err = json.Unmarshal(body, &tmp);'
  166. 'if err != nil'):
  167. out('return')
  168. with self.block('switch tmp.Tag'):
  169. for t in route.result_data_type.get_enumerated_subtypes():
  170. with self.block('case "%s":' % t.name, delim=(None, None)):
  171. self.emit('res = tmp.%s' % fmt_var(t.name))
  172. elif not is_void_type(route.result_data_type):
  173. with self.block('err = json.Unmarshal(body, &res);'
  174. 'if err != nil'):
  175. out('return')
  176. out()
  177. out('return')