Example #1
0
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
Example #2
0
    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)
Example #3
0
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)
Example #4
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
Example #5
0
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
Example #6
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')