def _generate_error_handling(self, namespace, route):
     out = self.emit
     style = route.attrs.get('style', 'rpc')
     with self.block('if resp.StatusCode == http.StatusConflict'):
         # If style was download, body was assigned to a header.
         # Need to re-read the response body to parse the error
         if style == 'download':
             out('defer resp.Body.Close()')
             with self.block('body, err = ioutil.ReadAll(resp.Body);'
                             'if err != nil'):
                 out('return')
         err_type = fmt_var(route.name) + 'APIError'
         if route.version != 1:
             err_type = '%sV%dAPIError' % (fmt_var(
                 route.name), route.version)
         out('var apiError %s' % err_type)
         with self.block('err = json.Unmarshal(body, &apiError);'
                         'if err != nil'):
             out('return')
         out('err = apiError')
         out('return')
     auth_ns = "" if namespace.name == "auth" else "auth."
     with self.block(
             'err = %sHandleCommonAuthErrors(dbx.Config, resp, body);'
             'if err != nil' % auth_ns):
         out('return')
     out('err = dropbox.HandleCommonAPIErrors(dbx.Config, resp, body)')
     out('return')
示例#2
0
    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):
            if route.deprecated is not None:
                out('log.Printf("WARNING: API `%s` is deprecated")' % fn)
                if route.deprecated.by is not None:
                    out('log.Printf("Use API `%s` instead")' %
                        fmt_var(route.deprecated.by.name))
                out()

            out('cli := dbx.Client')
            out()

            self._generate_request(namespace, route)
            self._generate_post()
            self._generate_response(route)
            ok_check = 'if resp.StatusCode == http.StatusOK'
            if fn == "Download":
                ok_check += ' || resp.StatusCode == http.StatusPartialContent'
            with self.block(ok_check):
                self._generate_result(route)
            self._generate_error_handling(route)

        out()
    def _generate_struct_builder(self, struct):
        fields = [
            "%s %s" %
            (fmt_var(field.name),
             fmt_type(field.data_type, struct.namespace, use_interface=True))
            for field in struct.all_required_fields
        ]
        self.emit('// New{0} returns a new {0} instance'.format(struct.name))
        signature = "func New{0}({1}) *{0}".format(struct.name,
                                                   ', '.join(fields))
        with self.block(signature):
            self.emit('s := new({0})'.format(struct.name))
            for field in struct.all_required_fields:
                field_name = fmt_var(field.name)
                self.emit("s.{0} = {0}".format(field_name))

            for field in struct.all_optional_fields:
                if field.has_default:
                    if is_primitive_type(field.data_type):
                        default = field.default
                        if is_boolean_type(field.data_type):
                            default = str(default).lower()
                        self.emit('s.{0} = {1}'.format(fmt_var(field.name),
                                                       default))
                    elif is_union_type(field.data_type):
                        self.emit('s.%s = &%s{Tagged:dropbox.Tagged{"%s"}}' %
                                  (fmt_var(field.name),
                                   fmt_type(field.data_type,
                                            struct.namespace).lstrip('*'),
                                   field.default.tag_name))
            self.emit('return s')
        self.emit()
    def _generate_struct_builder(self, struct):
        fields = ["%s %s" % (fmt_var(field.name),
                             fmt_type(field.data_type, struct.namespace,
                                      use_interface=True))
                  for field in struct.all_required_fields]
        self.emit('// New{0} returns a new {0} instance'.format(struct.name))
        signature = "func New{0}({1}) *{0}".format(struct.name, ', '.join(fields))
        with self.block(signature):
            self.emit('s := new({0})'.format(struct.name))
            for field in struct.all_required_fields:
                field_name = fmt_var(field.name)
                self.emit("s.{0} = {0}".format(field_name))

            for field in struct.all_optional_fields:
                if field.has_default:
                    if is_primitive_type(field.data_type):
                        default = field.default
                        if is_boolean_type(field.data_type):
                            default = str(default).lower()
                        self.emit('s.{0} = {1}'.format(fmt_var(field.name), default))
                    elif is_union_type(field.data_type):
                        self.emit('s.%s = &%s{Tagged:dropbox.Tagged{"%s"}}' %
                                  (fmt_var(field.name),
                                   fmt_type(field.data_type, struct.namespace).lstrip('*'),
                                   field.default.tag_name))
            self.emit('return s')
        self.emit()
    def _generate_base_type(self, base):
        t = fmt_type(base).lstrip('*')
        self.emit(
            '// Is{0} is the interface type for {0} and its subtypes'.format(
                t))
        with self.block('type Is%s interface' % t):
            self.emit('Is%s()' % t)
        self.emit()
        self.emit('// Is{0} implements the Is{0} interface'.format(t))
        self.emit("func (u *{0}) Is{0}() {{}}".format(t))
        self.emit()
        self._generate_union_helper(base)

        self.emit(
            "// Is{0}FromJSON converts JSON to a concrete Is{0} instance".
            format(t))
        with self.block(
                "func Is{0}FromJSON(data []byte) (Is{0}, error)".format(t)):
            name = fmt_var(t, export=False) + 'Union'
            self.emit("var t {0}".format(name))
            with self.block("if err := json.Unmarshal(data, &t); err != nil"):
                self.emit("return nil, err")
            with self.block("switch t.Tag"):
                fields = base.get_enumerated_subtypes()
                for field in fields:
                    with self.block('case "%s":' % field.name,
                                    delim=(None, None)):
                        self.emit("return t.{0}, nil".format(
                            fmt_var(field.name)))
            # FIX THIS
            self.emit("return nil, nil")
    def _generate_union_helper(self, u):
        name = u.name
        namespace = u.namespace
        fields = u.fields
        if is_struct_type(u) and u.has_enumerated_subtypes():
            name = fmt_var(name, export=False) + 'Union'
            fields = u.get_enumerated_subtypes()

        with self.block('type %s struct' % name):
            self.emit('dropbox.Tagged')
            for field in fields:
                if is_void_type(field.data_type):
                    continue
                self._generate_field(field, union_field=True,
                                     namespace=namespace)
        self.emit()
        self.emit('// Valid tag values for %s' % fmt_var(u.name))
        with self.block('const', delim=('(', ')')):
            for field in fields:
                self.emit('%s%s = "%s"' % (fmt_var(u.name), fmt_var(field.name), field.name))
        self.emit()

        num_void_fields = sum([is_void_type(f.data_type) for f in fields])
        # Simple structure, no need in UnmarshalJSON
        if len(fields) == num_void_fields:
            return

        self.emit('// UnmarshalJSON deserializes into a %s instance' % name)
        with self.block('func (u *%s) UnmarshalJSON(body []byte) error' % name):
            with self.block('type wrap struct'):
                self.emit('dropbox.Tagged')
                for field in fields:
                    if is_void_type(field.data_type) or \
                            is_primitive_type(field.data_type):
                        continue
                    self._generate_field(field, union_field=True,
                                         namespace=namespace, raw=True)
            self.emit('var w wrap')
            with self.block('if err := json.Unmarshal(body, &w); err != nil'):
                self.emit('return err')
            self.emit('u.Tag = w.Tag')
            with self.block('switch u.Tag'):
                for field in fields:
                    if is_void_type(field.data_type):
                        continue
                    field_name = fmt_var(field.name)
                    with self.block('case "%s":' % field.name, delim=(None, None)):
                        if is_union_type(field.data_type):
                            with self.block('if err := json.Unmarshal'
                                            '(w.{0}, &u.{0}); err != nil'
                                            .format(field_name)):
                                self.emit('return err')
                        else:
                            with self.block('if err := json.Unmarshal'
                                            '(body, &u.{0}); err != nil'
                                            .format(field_name)):
                                self.emit('return err')
            self.emit('return nil')
        self.emit()
    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(route)
            self._generate_response(route)
            with self.block('if resp.StatusCode == http.StatusOK'):
                self._generate_result(route)
            self._generate_error_handling(route)

        out()
 def _generate_error_handling(self, route):
     out = self.emit
     style = route.attrs.get('style', 'rpc')
     with self.block('if resp.StatusCode == http.StatusConflict'):
         # If style was download, body was assigned to a header.
         # Need to re-read the response body to parse the error
         if style == 'download':
             out('defer resp.Body.Close()')
             with self.block('body, err = ioutil.ReadAll(resp.Body);'
                             'if err != nil'):
                 out('return')
         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 || "
                     "resp.StatusCode == http.StatusInternalServerError"):
         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_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()
示例#10
0
 def _generate_field(self, field, union_field=False, namespace=None, raw=False):
     generate_doc(self, field)
     field_name = fmt_var(field.name)
     type_name = fmt_type(field.data_type, namespace, use_interface=True, raw=raw)
     json_tag = '`json:"%s"`' % field.name
     if is_nullable_type(field.data_type) or union_field:
         json_tag = '`json:"%s,omitempty"`' % field.name
     self.emit('%s %s %s' % (field_name, type_name, json_tag))
 def _generate_struct(self, struct):
     with self.block('type %s struct' % struct.name):
         if struct.parent_type:
             self.emit(
                 fmt_type(struct.parent_type, struct.namespace).lstrip('*'))
         for field in struct.fields:
             self._generate_field(field, namespace=struct.namespace)
         if struct.name in ('DownloadArg', ):
             self.emit(
                 '// ExtraHeaders can be used to pass Range, If-None-Match headers'
             )
             self.emit('ExtraHeaders map[string]string `json:"-"`')
     self._generate_struct_builder(struct)
     self.emit()
     if needs_base_type(struct):
         self.emit('// UnmarshalJSON deserializes into a %s instance' %
                   struct.name)
         with self.block('func (u *%s) UnmarshalJSON(b []byte) error' %
                         struct.name):
             with self.block('type wrap struct'):
                 for field in struct.all_fields:
                     self._generate_field(field,
                                          namespace=struct.namespace,
                                          raw=_needs_base_type(
                                              field.data_type))
             self.emit('var w wrap')
             with self.block('if err := json.Unmarshal(b, &w); err != nil'):
                 self.emit('return err')
             for field in struct.all_fields:
                 dt = field.data_type
                 fn = fmt_var(field.name)
                 tn = fmt_type(dt,
                               namespace=struct.namespace,
                               use_interface=True)
                 if _needs_base_type(dt):
                     if is_list_type(dt):
                         self.emit("u.{0} = make({1}, len(w.{0}))".format(
                             fn, tn))
                         # Grab the underlying type to get the correct Is...FromJSON method
                         tn = fmt_type(dt.data_type,
                                       namespace=struct.namespace,
                                       use_interface=True)
                         with self.block(
                                 "for i, e := range w.{0}".format(fn)):
                             self.emit("v, err := {1}FromJSON(e)".format(
                                 fn, tn))
                             with self.block('if err != nil'):
                                 self.emit('return err')
                             self.emit("u.{0}[i] = v".format(fn))
                     else:
                         self.emit("{0}, err := {1}FromJSON(w.{0})".format(
                             fn, tn))
                         with self.block('if err != nil'):
                             self.emit('return err')
                         self.emit("u.{0} = {0}".format(fn))
                 else:
                     self.emit("u.{0} = w.{0}".format(fn))
             self.emit('return nil')
示例#12
0
 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')
 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')
 def _generate_field(self, field, union_field=False, namespace=None, raw=False):
     generate_doc(self, field)
     field_name = fmt_var(field.name)
     type_name = fmt_type(field.data_type, namespace, use_interface=True)
     json_tag = '`json:"%s"`' % field.name
     if is_nullable_type(field.data_type) or union_field:
         json_tag = '`json:"%s,omitempty"`' % field.name
     if raw:
         self.emit('%s json.RawMessage %s' % (field_name, json_tag))
     else:
         self.emit('%s %s %s' % (field_name, type_name, json_tag))
示例#15
0
    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):
            out('dbx.Config.LogDebug("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' or style in ['upload', 'download']:
                headers["Dropbox-API-Arg"] = "dropbox.HTTPHeaderSafeJSON(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 sorted(headers.items()):
            out('\t"{}": {},'.format(k, v))
        out('}')
        if fmt_var(route.name) == "Download":
            out('for k, v := range arg.ExtraHeaders { headers[k] = v }')
        if auth != 'noauth' and auth != 'team':
            with self.block('if dbx.Config.AsMemberID != ""'):
                out('headers["Dropbox-API-Select-User"] = dbx.Config.AsMemberID'
                    )
        out()

        fn = route.name
        if route.version != 1:
            fn += '_v%d' % route.version
        authed = 'false' if auth == 'noauth' else 'true'
        out('req, err := (*dropbox.Context)(dbx).NewRequest("{}", "{}", {}, "{}", "{}", headers, {})'
            .format(host, style, authed, namespace.name, fn, body))
        with self.block('if err != nil'):
            out('return')

        out('dbx.Config.LogInfo("req: %v", req)')

        out()
    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)
示例#17
0
    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_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')
示例#19
0
 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_union_helper(self, u):
        name = u.name
        namespace = u.namespace
        fields = u.fields
        if is_struct_type(u) and u.has_enumerated_subtypes():
            name = fmt_var(name, export=False) + 'Union'
            fields = u.get_enumerated_subtypes()

        with self.block('type %s struct' % name):
            self.emit('dropbox.Tagged')
            for field in fields:
                if is_void_type(field.data_type):
                    continue
                self._generate_field(field,
                                     union_field=True,
                                     namespace=namespace)
        self.emit()
        self.emit('// Valid tag values for %s' % fmt_var(u.name))
        with self.block('const', delim=('(', ')')):
            for field in fields:
                self.emit('%s%s = "%s"' %
                          (fmt_var(u.name), fmt_var(field.name), field.name))
        self.emit()

        num_void_fields = sum([is_void_type(f.data_type) for f in fields])
        # Simple structure, no need in UnmarshalJSON
        if len(fields) == num_void_fields:
            return

        self.emit('// UnmarshalJSON deserializes into a %s instance' % name)
        with self.block('func (u *%s) UnmarshalJSON(body []byte) error' %
                        name):
            with self.block('type wrap struct'):
                self.emit('dropbox.Tagged')
                for field in fields:
                    if is_void_type(field.data_type) or \
                            is_primitive_type(field.data_type):
                        continue
                    self._generate_field(field,
                                         union_field=True,
                                         namespace=namespace,
                                         raw=True)
            self.emit('var w wrap')
            self.emit('var err error')
            with self.block('if err = json.Unmarshal(body, &w); err != nil'):
                self.emit('return err')
            self.emit('u.Tag = w.Tag')
            with self.block('switch u.Tag'):
                for field in fields:
                    if is_void_type(field.data_type):
                        continue
                    field_name = fmt_var(field.name)
                    with self.block('case "%s":' % field.name,
                                    delim=(None, None)):
                        if is_union_type(field.data_type):
                            self.emit(
                                'err = json.Unmarshal(w.{0}, &u.{0})'.format(
                                    field_name))
                        elif is_struct_type(field.data_type) and \
                            field.data_type.has_enumerated_subtypes():
                            self.emit(
                                "u.{0}, err = Is{1}FromJSON(body)".format(
                                    field_name, field.data_type.name))
                        else:
                            self.emit(
                                'err = json.Unmarshal(body, &u.{0})'.format(
                                    field_name))
                    with self.block("if err != nil"):
                        self.emit("return err")
            self.emit('return nil')
        self.emit()
示例#21
0
    def _generate_route(self, namespace, route):
        out = self.emit

        route_name = route.name
        if route.version != 1:
            route_name += '_v%d' % route.version

        fn = fmt_var(route.name)
        if route.version != 1:
            fn += 'V%d' % route.version

        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):
            if route.deprecated is not None:
                out('log.Printf("WARNING: API `%s` is deprecated")' % fn)
                if route.deprecated.by is not None:
                    replacement_fn = fmt_var(route.deprecated.by.name)
                    if route.deprecated.by.version != 1:
                        replacement_fn += "V%d" % route.deprecated.by.version
                    out('log.Printf("Use API `%s` instead")' % replacement_fn)
                out()

            args = {
                "Host": route.attrs.get('host', 'api'),
                "Namespace": namespace.name,
                "Route": route_name,
                "Auth": route.attrs.get('auth', ''),
                "Style": route.attrs.get('style', 'rpc'),
            }

            with self.block('req := dropbox.Request'):
                for k, v in args.items():
                    out(k + ':"' + v + '",')

                out("Arg: {arg},".format(
                    arg="arg"
                    if not is_void_type(route.arg_data_type) else "nil"))
                out("ExtraHeaders: {headers},".format(
                    headers="arg.ExtraHeaders" if fmt_var(route.name) ==
                    "Download" else "nil"))
            out()

            out("var resp []byte")
            out("var respBody io.ReadCloser")
            out("resp, respBody, err = (*dropbox.Context)(dbx).Execute(req, {body})"
                .format(body="content" if route.attrs.get('style', '') ==
                        'upload' else "nil"))
            with self.block("if err != nil"):
                out("var appErr {fn}APIError".format(fn=fn))
                out("err = {auth}ParseError(err, &appErr)".format(
                    auth="auth." if namespace.name != "auth" else ""))
                with self.block("if err == &appErr"):
                    out("err = appErr")
                out("return")
            out()

            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(resp, &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(resp, &res);'
                                'if err != nil'):
                    out('return')
                out()
            else:
                out("_ = resp")

            if route.attrs.get('style', 'rpc') == "download":
                out("content = respBody")
            else:
                out("_ = respBody")
            out('return')
        out()
    def _generate_union_helper(self, u):
        name = u.name
        namespace = u.namespace
        # Unions can be inherited, but don't need to be polymorphic.
        # So let's flatten out all the inherited fields.
        fields = u.all_fields
        if is_struct_type(u) and u.has_enumerated_subtypes():
            name = fmt_var(name, export=False) + 'Union'
            fields = u.get_enumerated_subtypes()

        with self.block('type %s struct' % name):
            self.emit('dropbox.Tagged')
            for field in fields:
                if is_void_type(field.data_type):
                    continue
                self._generate_field(field, union_field=True,
                                     namespace=namespace)
        self.emit()
        self.emit('// Valid tag values for %s' % fmt_var(u.name))
        with self.block('const', delim=('(', ')')):
            for field in fields:
                self.emit('%s%s = "%s"' %
                          (fmt_var(u.name), fmt_var(field.name), field.name))
        self.emit()

        num_void_fields = sum([is_void_type(f.data_type) for f in fields])
        # Simple structure, no need in UnmarshalJSON
        if len(fields) == num_void_fields:
            return

        self.emit('// UnmarshalJSON deserializes into a %s instance' % name)
        with self.block('func (u *%s) UnmarshalJSON(body []byte) error' % name):
            with self.block('type wrap struct'):
                self.emit('dropbox.Tagged')
                for field in fields:
                    if is_void_type(field.data_type) or (
                            is_struct_type(field.data_type) and not _needs_base_type(field.data_type)):
                        # pure structures are flattened in the containing union json blob and thus are loaded from body
                        continue
                    # sub-unions must be handled as RawMessage, which will be loaded into correct implementation later
                    self._generate_field(field, union_field=True,
                                         namespace=namespace, raw=_needs_base_type(field.data_type))
            self.emit('var w wrap')
            self.emit('var err error')
            with self.block('if err = json.Unmarshal(body, &w); err != nil'):
                self.emit('return err')
            self.emit('u.Tag = w.Tag')
            with self.block('switch u.Tag'):
                for field in fields:
                    if is_void_type(field.data_type):
                        continue
                    field_name = fmt_var(field.name)
                    with self.block('case "%s":' % field.name, delim=(None, None)):
                        if _needs_base_type(field.data_type):
                            self.emit("u.{0}, err = Is{1}FromJSON(w.{0})"
                                      .format(field_name, field.data_type.name))
                        elif is_struct_type(field.data_type):
                            self.emit('err = json.Unmarshal(body, &u.{0})'
                                      .format(field_name))
                        else:
                            self.emit('u.{0} = w.{0}'.format(field_name))
                    with self.block("if err != nil"):
                        self.emit("return err")
            self.emit('return nil')
        self.emit()