123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- import os
- from stone.generator import CodeGenerator
- from stone.data_type import (
- is_void_type,
- is_struct_type
- )
- from go_helpers import (
- HEADER,
- fmt_type,
- fmt_var,
- generate_doc,
- )
- class GoClientGenerator(CodeGenerator):
- def generate(self, api):
- for namespace in api.namespaces.values():
- if len(namespace.routes) > 0:
- self._generate_client(namespace)
- def _generate_client(self, namespace):
- file_name = os.path.join(self.target_folder_path, namespace.name,
- 'client.go')
- with self.output_to_relative_path(file_name):
- self.emit_raw(HEADER)
- self.emit()
- self.emit('package %s' % namespace.name)
- self.emit()
- self.emit('// Client interface describes all routes in this namespace')
- with self.block('type Client interface'):
- for route in namespace.routes:
- generate_doc(self, route)
- self.emit(self._generate_route_signature(namespace, route))
- self.emit()
- self.emit('type apiImpl dropbox.Context')
- for route in namespace.routes:
- self._generate_route(namespace, route)
- self.emit('// New returns a Client implementation for this namespace')
- with self.block('func New(c dropbox.Config) *apiImpl'):
- self.emit('ctx := apiImpl(dropbox.NewContext(c))')
- self.emit('return &ctx')
- def _generate_route_signature(self, namespace, route):
- req = fmt_type(route.arg_data_type, namespace)
- res = fmt_type(route.result_data_type, namespace, use_interface=True)
- fn = fmt_var(route.name)
- style = route.attrs.get('style', 'rpc')
- arg = '' if is_void_type(route.arg_data_type) else 'arg {req}'
- ret = '(err error)' if is_void_type(route.result_data_type) else \
- '(res {res}, err error)'
- signature = '{fn}(' + arg + ') ' + ret
- if style == 'download':
- signature = '{fn}(' + arg + \
- ') (res {res}, content io.ReadCloser, err error)'
- elif style == 'upload':
- signature = '{fn}(' + arg + ', content io.Reader) ' + ret
- if is_void_type(route.arg_data_type):
- signature = '{fn}(content io.Reader) ' + ret
- return signature.format(fn=fn, req=req, res=res)
- def _generate_route(self, namespace, route):
- out = self.emit
- fn = fmt_var(route.name)
- err = fmt_type(route.error_data_type, namespace)
- out('//%sAPIError is an error-wrapper for the %s route' %
- (fn, route.name))
- with self.block('type {fn}APIError struct'.format(fn=fn)):
- out('dropbox.APIError')
- out('EndpointError {err} `json:"error"`'.format(err=err))
- out()
- signature = 'func (dbx *apiImpl) ' + self._generate_route_signature(
- namespace, route)
- with self.block(signature):
- out('cli := dbx.Client')
- out()
- self._generate_request(namespace, route)
- self._generate_post()
- self._generate_response(route)
- with self.block('if resp.StatusCode == http.StatusOK'):
- self._generate_result(route)
- self._generate_error_handling(route)
- out()
- def _generate_request(self, namespace, route):
- out = self.emit
- auth = route.attrs.get('auth', '')
- host = route.attrs.get('host', 'api')
- style = route.attrs.get('style', 'rpc')
- body = 'nil'
- if not is_void_type(route.arg_data_type):
- with self.block('if dbx.Config.Verbose'):
- out('log.Printf("arg: %v", arg)')
- out('b, err := json.Marshal(arg)')
- with self.block('if err != nil'):
- out('return')
- out()
- if host != 'content':
- body = 'bytes.NewReader(b)'
- if style == 'upload':
- body = 'content'
- headers = {}
- if not is_void_type(route.arg_data_type):
- if host == 'content':
- headers["Dropbox-API-Arg"] = "string(b)"
- else:
- headers["Content-Type"] = '"application/json"'
- if style == 'upload':
- headers["Content-Type"] = '"application/octet-stream"'
- out('headers := map[string]string{')
- for k, v in headers.items():
- out('\t"{}": {},'.format(k, v))
- out('}')
- if auth != 'noauth' and auth != 'team':
- with self.block('if dbx.Config.AsMemberID != ""'):
- out('headers["Dropbox-API-Select-User"] = dbx.Config.AsMemberID')
- out()
- authed = 'false' if auth == 'noauth' else 'true'
- out('req, err := (*dropbox.Context)(dbx).NewRequest("{}", "{}", {}, "{}", "{}", headers, {})'.format(
- host, style, authed, namespace.name, route.name, body))
- with self.block('if err != nil'):
- out('return')
- with self.block('if dbx.Config.Verbose'):
- out('log.Printf("req: %v", req)')
- out()
- def _generate_post(self):
- out = self.emit
- out('resp, err := cli.Do(req)')
- with self.block('if dbx.Config.Verbose'):
- out('log.Printf("resp: %v", resp)')
- with self.block('if err != nil'):
- out('return')
- out()
- def _generate_response(self, route):
- out = self.emit
- style = route.attrs.get('style', 'rpc')
- if style == 'download':
- out('body := []byte(resp.Header.Get("Dropbox-API-Result"))')
- out('content = resp.Body')
- else:
- out('defer resp.Body.Close()')
- with self.block('body, err := ioutil.ReadAll(resp.Body);'
- 'if err != nil'):
- out('return')
- out()
- with self.block('if dbx.Config.Verbose'):
- out('log.Printf("body: %s", body)')
- def _generate_error_handling(self, route):
- out = self.emit
- with self.block('if resp.StatusCode == http.StatusConflict'):
- out('var apiError %sAPIError' % fmt_var(route.name))
- with self.block('err = json.Unmarshal(body, &apiError);'
- 'if err != nil'):
- out('return')
- out('err = apiError')
- out('return')
- out('var apiError dropbox.APIError')
- with self.block('if resp.StatusCode == http.StatusBadRequest'):
- out('apiError.ErrorSummary = string(body)')
- out('err = apiError')
- out('return')
- with self.block('err = json.Unmarshal(body, &apiError);'
- 'if err != nil'):
- out('return')
- out('err = apiError')
- out('return')
- def _generate_result(self, route):
- out = self.emit
- if is_struct_type(route.result_data_type) and \
- route.result_data_type.has_enumerated_subtypes():
- out('var tmp %sUnion' % fmt_var(route.result_data_type.name, export=False))
- with self.block('err = json.Unmarshal(body, &tmp);'
- 'if err != nil'):
- out('return')
- with self.block('switch tmp.Tag'):
- for t in route.result_data_type.get_enumerated_subtypes():
- with self.block('case "%s":' % t.name, delim=(None, None)):
- self.emit('res = tmp.%s' % fmt_var(t.name))
- elif not is_void_type(route.result_data_type):
- with self.block('err = json.Unmarshal(body, &res);'
- 'if err != nil'):
- out('return')
- out()
- out('return')
|