Ejemplo n.º 1
0
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__))
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
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)))
Ejemplo n.º 4
0
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)
Ejemplo n.º 5
0
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")
Ejemplo n.º 6
0
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
Ejemplo n.º 7
0
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')