def _write_encoder(typedef: Typedef, fid: TextIO) -> None: """ Write the Encoder in the Elm code. :param typedef: to be encoded :param fid: target :return: """ if typedef.identifier == '': raise ValueError( "Expected a typedef with an identifier, but got a typedef with an empty identifier." ) if isinstance(typedef, Recorddef): obj_name = 'a' + typedef.identifier fid.write("encode{0} : {0} -> Json.Encode.Value\n".format( typedef.identifier)) fid.write("encode{} {} =\n".format(typedef.identifier, obj_name)) fid.write(INDENT + 'Json.Encode.object\n') prefix = 2 * INDENT + '[ ' for prop in typedef.properties.values(): fid.write( prefix + '( "{}", '.format(swagger_to.snake_case(identifier=prop.name))) if prop.typedef is None: raise ValueError( "Unexpected None typedef of prop {!r} in type {!r}".format( prop.name, typedef.identifier)) encoder = _type_encoder(typedef=prop.typedef, path='{}.{}'.format( typedef.identifier, prop.name)) if not prop.required: fid.write('Json.Encode.Extra.maybe ({}) '.format(encoder)) else: fid.write('{} '.format(encoder)) fid.write('<| {}.{} )\n'.format( obj_name, swagger_to.camel_case(identifier=prop.name))) prefix = 2 * INDENT + ', ' fid.write(2 * INDENT + "]") elif isinstance(typedef, (Booldef, Intdef, Floatdef, Stringdef, Listdef, Dictdef)): bracketed_type_expression = _argument_expression( typedef=typedef, path=typedef.identifier) var_name = 'a' + typedef.identifier fid.write("encode{} : {} -> Json.Encode.Value \n".format( typedef.identifier, bracketed_type_expression)) fid.write("encode{} {} =\n".format(typedef.identifier, var_name)) fid.write(INDENT + "{}".format( _type_encoder(typedef=typedef, path=typedef.identifier))) fid.write(" <| {}".format(var_name)) else: raise AssertionError("Unexpected type {}.".format(typedef.__class__))
def test_camel_case(self): table = [ ('CvSizeInt', 'cvSizeInt'), ('URLsToFind', 'urlsToFind'), ('IDsToFind', 'idsToFind'), ('some_ids', 'someIDs'), ('someIDs', 'someIDs'), ] for an_input, expected in table: got = swagger_to.camel_case(identifier=an_input) self.assertEqual(expected, got)
def _write_type_definition(typedef: Typedef, fid: TextIO) -> None: """ Write the type definition in the Elm code. :param typedef: to be declared :param fid: target :return: """ if typedef.identifier == '': raise ValueError( "Expected a typedef with an identifier, but got a typedef with an empty identifier." ) if typedef.description: _write_top_level_description(description=typedef.description, fid=fid) fid.write('\n') if isinstance(typedef, Recorddef): fid.write("type alias {} = \n".format(typedef.identifier)) prefix = '{ ' for prop in typedef.properties.values(): if prop.description: _write_description(description=prop.description, indent=INDENT, fid=fid) fid.write("\n") if prop.typedef is None: raise ValueError( "Unexpected None typedef of prop {!r} in type {!r}".format( prop.name, typedef.identifier)) type_expr = _type_expression(typedef=prop.typedef, path='{}.{}'.format( typedef.identifier, prop.name)) camel_case_name = swagger_to.camel_case(identifier=prop.name) if not prop.required: fid.write( INDENT + prefix + '{} : Maybe ({})\n'.format(camel_case_name, type_expr)) else: fid.write(INDENT + prefix + '{} : {}\n'.format(camel_case_name, type_expr)) prefix = ', ' fid.write(INDENT + "}") else: fid.write("type alias {} = \n".format(typedef.identifier)) fid.write(INDENT + "{}".format( _type_expression(typedef=typedef, path=typedef.identifier)))
def _request_function_name(operation_id: str) -> str: """ Generate the name of the function which will send the request based on the operation ID. :param operation_id: ID of the operation from the Swagger spec :return: Valid Elm identifier >>> _request_function_name('CamelCase') 'camelCase' >>> _request_function_name('snake_case') 'snakeCase' >>> _request_function_name('Snake_case') 'snakeCase' >>> _request_function_name('Dash-Case') 'dashCase' >>> _request_function_name('dash-case') 'dashCase' >>> _request_function_name('_snake_case') '_snakeCase' >>> _request_function_name('snake_case_') 'snakeCase_' >>> _request_function_name('__') '__' >>> _request_function_name('test.me') 'testMe' >>> _request_function_name('test.me.some.more') 'testMeSomeMore' """ identifier = operation_id.replace('.', '_') return swagger_to.camel_case(identifier)
def _write_header(fid: TextIO, typedefs: MutableMapping[str, Typedef], requests: List[Request]) -> None: """ Write the header. :param fid: target :param typedefs: translated type definitions :param requests: translated request functions :return: """ fid.write( "-- Automatically generated file by swagger_to. DO NOT EDIT OR APPEND ANYTHING!\n\n\n" ) to_expose = [] # Type: List[str] for typedef in typedefs.values(): to_expose.append(typedef.identifier) to_expose.append('decode{}'.format(typedef.identifier)) to_expose.append('encode{}'.format(typedef.identifier)) for request in requests: to_expose.append('{}Request'.format( swagger_to.camel_case(identifier=request.operation_id))) to_expose.sort() joinstr = '\n' + INDENT * 2 + ', ' fid.write("module Client \n") fid.write(INDENT + "exposing\n") fid.write(INDENT * 2 + "( {}\n".format(joinstr.join(to_expose))) fid.write(INDENT * 2 + ")\n\n") fid.write("import Dict exposing (Dict)\n" "import Http\n" "import Json.Decode\n" "import Json.Decode.Pipeline\n" "import Json.Encode\n" "import Json.Encode.Extra\n" "import Time\n\n\n")
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 _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')