def _check_recursively_cases(typedef: swagger_to.intermediate.Typedef, visited: MutableSet[swagger_to.intermediate.Typedef]) -> List[Complaint]: """ Check the typedef's adherence to the casing conventions. :param typedef: to be translated :param visited: already seen typedefs :return: the list of failed checks """ complaints = [] # type: List[Complaint] if typedef in visited: return complaints visited.add(typedef) if isinstance(typedef, swagger_to.intermediate.Primitivedef): pass elif isinstance(typedef, swagger_to.intermediate.Arraydef): if typedef.identifier != "" and typedef.identifier != swagger_to.capital_camel_case(typedef.identifier): complaints.append( Complaint( message="Not a capital camel case identifier (e.g. CamelCase)", what=typedef.identifier, where="In array {}".format(typedef.identifier), line=typedef.line)) complaints.extend(_check_recursively_cases(typedef=typedef.items, visited=visited)) elif isinstance(typedef, swagger_to.intermediate.Mapdef): if typedef.identifier != "" and typedef.identifier != swagger_to.capital_camel_case(typedef.identifier): complaints.append( Complaint( message="Not a capital camel case identifier (e.g. CamelCase)", what=typedef.identifier, where="In map {}".format(typedef.identifier), line=typedef.line)) complaints.extend(_check_recursively_cases(typedef=typedef.values, visited=visited)) elif isinstance(typedef, swagger_to.intermediate.Objectdef): if typedef.identifier != "" and typedef.identifier != swagger_to.capital_camel_case(typedef.identifier): complaints.append( Complaint( message="Not a capital camel case identifier (e.g. CamelCase)", what=typedef.identifier, where="In object {}".format(typedef.identifier), line=typedef.line)) for prop in typedef.properties.values(): if prop.name != swagger_to.snake_case(prop.name): complaints.append( Complaint( message="Not a snake case identifier (e.g. snake_case)", what=prop.name, where="In object {}, property {}".format(typedef.identifier, prop.name), line=typedef.line)) complaints.extend(_check_recursively_cases(typedef=prop.typedef, visited=visited)) return complaints
def test_capial_camel_case(self): table = [ ('CvSizeInt', 'CvSizeInt'), ('some_urls', 'SomeURLs'), ('someURLs', 'SomeURLs'), ('some_ids', 'SomeIDs'), ('someIDs', 'SomeIDs'), ] for an_input, expected in table: got = swagger_to.capital_camel_case(identifier=an_input) self.assertEqual(expected, got)
def _anonymous_or_get_typedef(intermediate_typedef: swagger_to.intermediate.Typedef, typedefs: MutableMapping[str, Typedef]) -> Typedef: """Create an anonymous type definition or retrieve the type definition from the existing definition table.""" if intermediate_typedef.identifier != '': identifier = swagger_to.capital_camel_case(identifier=intermediate_typedef.identifier) if not identifier in typedefs: raise ValueError("Undefined Go type for intermediate typedef {!r}: {!r}".format( intermediate_typedef.identifier, identifier)) return typedefs[identifier] return _to_typedef(intermediate_typedef=intermediate_typedef)
def _to_route(endpoint: swagger_to.intermediate.Endpoint, typedefs: MutableMapping[str, Typedef]) -> Route: """ Convert an intermediate representation of an endpoint to a muxing route of Go server stub. :param endpoint: intermediate representation of an endpoint :param typedefs: table of type definitions :return: converted route """ route = Route() route.method = endpoint.method.lower() route.path = _endpoint_to_route_path(endpoint=endpoint) route.description = endpoint.description ## # Determine handable parameters ## handable_parameters = [] # type: List[swagger_to.intermediate.Parameter] for param in endpoint.parameters: # Assert that we can handle all the supplied parameters. if param.in_what == 'formData': # No code is generated for the parameters in the form data since there are so many edge cases # which we possibly can't cover. continue elif param.in_what in ['query', 'body', 'path', 'header']: handable_parameters.append(param) else: raise NotImplementedError( "Handling of parameters in {} is not implemented yet: endpoint {} {}, parameter {}." .format(param.in_what, endpoint.path, endpoint.method, param.name)) ## # Generate identifiers corresponding to the parameters. ## param_to_identifier = { param: swagger_to.camel_case(identifier=param.name) for param in handable_parameters } # Add the location as prefix if the argument identifiers overlap identifiers = list(param_to_identifier.values()) needs_location_prefix = len(set(identifiers)) != len(identifiers) if needs_location_prefix: param_to_identifier = { param: swagger_to.camel_case( identifier="{}_{}".format(param.in_what, param.name)) for param in endpoint.parameters } ## # Assert that there are no conflicts at this point ## by_identifier = collections.defaultdict( list ) # type: MutableMapping[str, List[swagger_to.intermediate.Parameter]] for param, identifier in param_to_identifier.items(): by_identifier[identifier].append(param) # yapf: disable msgs = [ "in the endpoint {} {} for the identifier {!r}: {}".format( endpoint.method.upper(), endpoint.path, identifier, ", ".join( ["{} in {}".format(param.name, param.in_what) for param in params])) for identifier, params in by_identifier.items() if len(params) > 1 ] # yapf: enable if len(msgs) > 0: raise ValueError( "There are conflicting identifiers for parameters:\n{}".format( "\n".join(msgs))) ## # Convert parameters to arguments ## assert all(param in param_to_identifier for param in handable_parameters), \ "Expected all parameters to have a generated argument identifier." for param in handable_parameters: identifier = param_to_identifier[param] argument = Argument() argument.typedef = _anonymous_or_get_typedef( intermediate_typedef=param.typedef, typedefs=typedefs) argument.required = param.required if not param.required and isinstance(argument.typedef, Primitivedef): pointer_typedef = Pointerdef() pointer_typedef.identifier = argument.typedef.identifier pointer_typedef.description = argument.typedef.description pointer_typedef.pointed = argument.typedef argument.typedef = pointer_typedef argument.parameter_name = param.name argument.identifier = identifier argument.in_what = param.in_what argument.parsing_identifier = swagger_to.camel_case(identifier='a_' + identifier) if param.json_schema is not None: argument.json_schema = _to_json_schema( intermediate_schema=param.json_schema) if argument.in_what == 'header': route.wrapper.header_arguments.append(argument) elif argument.in_what == 'query': route.wrapper.query_arguments.append(argument) elif argument.in_what == 'body': route.wrapper.body_argument = argument elif argument.in_what == 'path': route.wrapper.path_arguments.append(argument) else: raise AssertionError("Unexpected argument given in: {}".format( argument.in_what)) route.handler.arguments.append(argument) ## # Determine route attributes ## route.wrapper.identifier = swagger_to.capital_camel_case( identifier='wrap_' + endpoint.operation_id) route.wrapper.handler = route.handler route.handler.identifier = swagger_to.capital_camel_case( identifier=endpoint.operation_id) return route
def _to_typedef( intermediate_typedef: swagger_to.intermediate.Typedef) -> Typedef: """Convert intermediate type definition into a type definition suitable for Go code generation.""" typedef = None # type: Union[None, Typedef] if isinstance(intermediate_typedef, swagger_to.intermediate.Primitivedef): typedef = Primitivedef() if intermediate_typedef.type == 'string': if intermediate_typedef.format == 'date-time': typedef.type = 'time.Time' else: typedef.type = 'string' elif intermediate_typedef.type == 'number': if intermediate_typedef.format is None: typedef.type = 'float64' elif intermediate_typedef.format == 'float': typedef.type = 'float32' elif intermediate_typedef.format == 'double': typedef.type = 'float64' else: raise ValueError("Unexpected format {!r} for type {!r}".format( intermediate_typedef.format, intermediate_typedef.type)) elif intermediate_typedef.type == 'integer': if intermediate_typedef.format is None: typedef.type = 'int' elif intermediate_typedef.format == 'int32': typedef.type = 'int32' elif intermediate_typedef.format == 'int64': typedef.type = 'int64' else: raise ValueError("Unexpected format {!r} for type {!r}".format( intermediate_typedef.format, intermediate_typedef.type)) elif intermediate_typedef.type == 'boolean': typedef.type = 'bool' else: raise NotImplementedError( "Unhandled translation of a primitive intermediate type to Go with 'type': {!r}" .format(intermediate_typedef.type)) elif isinstance(intermediate_typedef, swagger_to.intermediate.Arraydef): if intermediate_typedef.items is None: raise ValueError( "Unexpected intermediate type definition of an array to have items None: {!r}" .format(intermediate_typedef.identifier)) typedef = Arraydef() typedef.items = _to_typedef( intermediate_typedef=intermediate_typedef.items) elif isinstance(intermediate_typedef, swagger_to.intermediate.Mapdef): typedef = Mapdef() typedef.values = _to_typedef( intermediate_typedef=intermediate_typedef.values) elif isinstance(intermediate_typedef, swagger_to.intermediate.Objectdef): typedef = Structdef() for propdef in intermediate_typedef.properties.values(): field = Fielddef() field_typedef = _to_typedef(intermediate_typedef=propdef.typedef) if not propdef.name in intermediate_typedef.required and isinstance( field_typedef, Primitivedef): optional_field_typedef = Pointerdef() optional_field_typedef.pointed = field_typedef field_typedef = optional_field_typedef field.typedef = field_typedef field.description = propdef.description field.json_name = propdef.name field.name = swagger_to.capital_camel_case(identifier=propdef.name) typedef.fields[field.name] = field if propdef.name in intermediate_typedef.required: typedef.required.append(field.name) elif isinstance(intermediate_typedef, swagger_to.intermediate.AnyValuedef): typedef = Interfacedef() else: raise NotImplementedError( "Unhandled translation of an intermediate type to Go: {!r}".format( type(intermediate_typedef))) assert typedef is not None if intermediate_typedef.identifier != '': typedef.identifier = swagger_to.capital_camel_case( identifier=intermediate_typedef.identifier) typedef.description = intermediate_typedef.description typedef.json_schema = _to_json_schema( intermediate_schema=intermediate_typedef.json_schema) return typedef
def _write_request(request: Request, fid: TextIO) -> None: """ Generate the code of the request function. :param request: function definition :param fid: target :return: """ # pylint: disable=too-many-locals # pylint: disable=too-many-statements # pylint: disable=too-many-branches description = 'Contains a "{}" request to the endpoint: `{}`, to be sent with Http.send'.format( request.method, request.path) if request.description: description += '\n\n' + request.description _write_top_level_description(description=description, fid=fid) fid.write('\n') types = [] # type List[str] names = [] # type List[str] for param in request.parameters: if param.typedef is None: raise ValueError( "Unexpected None typedef of param {!r} in request {!r}".format( param.name, request.operation_id)) if param.required: types.append(_type_expression(typedef=param.typedef)) names.append(swagger_to.camel_case(identifier=param.name)) else: types.append('Maybe {}'.format( _type_expression(typedef=param.typedef))) names.append('maybe{}'.format( swagger_to.capital_camel_case(param.name))) return_type = None # type: Optional[str] return_type_decoder = None # type: Optional[str] if '200' in request.responses: resp = request.responses['200'] if resp.typedef is not None: return_type = _argument_expression(typedef=resp.typedef) return_type_decoder = _argument_decoder_expression( typedef=resp.typedef) # function signature and arguments function_name = '{}Request'.format( _request_function_name(request.operation_id)) types = ["String", "Maybe Time.Time", "Bool"] + types names = ["prefix", "maybeTimeout", "withCredentials"] + names types_str = ' -> '.join(types) types_str += ' -> ' names_str = ' '.join(names) line1 = function_name + ' : ' + types_str if return_type is None: line1 += 'Http.Request String\n' else: line1 += 'Http.Request {}\n'.format(return_type) line2 = function_name + ' ' + names_str + ' =' indent = 1 if len(line1) <= 120: fid.write(line1) fid.write(line2) else: fid.write(function_name + ' :\n') join_str = '\n' + INDENT + '-> ' fid.write(INDENT + '{}\n'.format(join_str.join(types))) if return_type is None: fid.write(INDENT + '-> Http.Request String\n') else: fid.write(INDENT + '-> Http.Request {}\n'.format(return_type)) if len(line2) > 120: fid.write(function_name) join_str = '\n' + INDENT fid.write('\n' + INDENT + '{}'.format(join_str.join(names))) fid.write(' =') indent = 2 else: fid.write(line2) fid.write('\n') name_to_parameters = {param.name: param for param in request.parameters} rel_path = request.path[1:] if request.path.startswith( '/') else request.path fid.write(INDENT * indent + 'let\n') # path parameters token_pth = swagger_to.tokenize_path(path=rel_path) url_name = 'baseUrl' if request.query_parameters else 'url' if not token_pth.parameter_to_token_indices: fid.write(INDENT * (indent + 1) + '{} = prefix ++ "{}"\n'.format(url_name, rel_path)) else: fid.write(INDENT * (indent + 1) + '{} = prefix'.format(url_name)) for i, tkn in enumerate(token_pth.tokens): fid.write("\n") if i in token_pth.token_index_to_parameter: param_name = token_pth.token_index_to_parameter[i] param = name_to_parameters[param_name] camel_case_name = swagger_to.camel_case(identifier=param.name) if not isinstance(param.typedef, Stringdef): camel_case_name = '(toString {})'.format(camel_case_name) fid.write(INDENT * (indent + 2) + '++ {}'.format(camel_case_name)) else: # escape special characters fid.write(INDENT * (indent + 2) + '++ "{}"'.format(_escape_string(text=tkn))) if request.path_parameters and request.query_parameters: fid.write("\n") # query parameters if request.query_parameters: required = [] # type: List[str] not_required = [] # type: List[str] for i, param in enumerate(request.query_parameters): if param.required: arg_name = swagger_to.camel_case(identifier=param.name) arg = arg_name if not isinstance(param.typedef, Stringdef): arg = '(toString {})'.format(arg_name) required.append('("' + param.name + '", ' + arg + ')') else: arg_name = 'maybe{}'.format( swagger_to.capital_camel_case(identifier=param.name)) arg = arg_name if not isinstance(param.typedef, Stringdef): arg = '(Maybe.map toString {})'.format(arg_name) not_required.append('("' + param.name + '", ' + arg + ')') fid.write(INDENT * (indent + 1) + 'queryString = \n') fid.write(INDENT * (indent + 2) + 'paramsToQuery\n') if required: fid.write((INDENT * (indent + 3)) + "[ ") for i, tuple_str in enumerate(required): if i == 0: fid.write(tuple_str + "\n") else: fid.write((INDENT * (indent + 3)) + ", " + tuple_str + "\n") fid.write((INDENT * (indent + 3)) + "]\n") else: fid.write((INDENT * (indent + 3)) + '[]\n') if not_required: fid.write((INDENT * (indent + 3)) + "[ ") for i, tuple_str in enumerate(not_required): if i == 0: fid.write(tuple_str + "\n") else: fid.write((INDENT * (indent + 3)) + ", " + tuple_str + "\n") fid.write((INDENT * (indent + 3)) + "]\n") else: fid.write((INDENT * (indent + 3)) + '[]\n') fid.write(INDENT * (indent + 1) + 'url = baseUrl ++ queryString\n') fid.write(indent * INDENT + 'in\n') mth = request.method.upper() fid.write(INDENT * indent + 'Http.request\n') fid.write(INDENT * (indent + 1) + '{ body = ') if request.body_parameter is not None: if request.body_parameter.typedef is None: raise ValueError( "Unexpected None typedef of body_parameter in request {!r}". format(request.operation_id)) if not request.body_parameter.required: fid.write('Json.Encode.Extra.maybe ({}'.format( _type_encoder(request.body_parameter.typedef))) fid.write('({}'.format(_type_encoder(request.body_parameter.typedef))) fid.write(' {}) |> Http.jsonBody'.format( swagger_to.camel_case(identifier=request.body_parameter.name))) else: fid.write('Http.emptyBody') fid.write('\n') if return_type is None: fid.write(INDENT * (indent + 1) + ', expect = Http.expectString\n') else: fid.write( INDENT * (indent + 1) + ', expect = Http.expectJson {}\n'.format(return_type_decoder)) fid.write(INDENT * (indent + 1) + ', headers = []\n') fid.write(INDENT * (indent + 1) + ', method = "{}"\n'.format(mth)) fid.write(INDENT * (indent + 1) + ', timeout = maybeTimeout\n') fid.write(INDENT * (indent + 1) + ', url = url\n') fid.write(INDENT * (indent + 1) + ', withCredentials = withCredentials\n') fid.write(INDENT * (indent + 1) + '}\n')