# NOTE
# THIS MODULE IS FOR REFERENCE ONLY
# AND IS NO LONGER COMPATIBLE WITH THE LATEST CODEBASE

"""
    Utils for creating swagger spec from a config_web module.

"""
from collections import OrderedDict

import yaml


def setup_yaml():
    """https://stackoverflow.com/a/8661021"""
    represent_dict_order = lambda self, data: self.represent_mapping("tag:yaml.org,2002:map", data.items())
    yaml.add_representer(OrderedDict, represent_dict_order)


all_params = {}


def _get_common_parameters(settings, biothing_object, url):
    _all = _get_all_params(settings)
    _ret = OrderedDict([])
    # get all params except for q, scopes, ids -> these are added to the request body, and not the query string
    _common_params_list = [
        (k, v)
        for (k, v) in _all.items()
        if (k != "q") and (k != "scopes") and (k != "ids") and (k in getattr(settings, "KWARG_DESCRIPTIONS", {}))
    ]
    # add 'special' parameters not controlled by main biothings web pipeline
    _common_params_list.extend(
        [("callback", {"type": str, "default": None}), ("email", {"type": str, "default": None})]
    )
    _description_templates = getattr(settings, "KWARG_DESCRIPTIONS", {})
    for param, _description in _common_params_list:
        _description_template_object = _description_templates.get(param)
        _param_display = _description_template_object.get("name")
        _ret.update(
            OrderedDict(
                [
                    (
                        _param_display,
                        OrderedDict(
                            [
                                ("name", _param_display),
                                ("in", "query"),
                                (
                                    "description",
                                    _description_template_object.get("text_template", "").format(
                                        biothing_object=biothing_object,
                                        param_type=_string_types(_all.get(param, {}).get("type", "")),
                                        param_default_value=_param_default_values(
                                            _all.get(param, {}).get("default", "")
                                        ),
                                        param_max=_param_max(_all.get(param, {}).get("max", "")),
                                    ),
                                ),
                                ("schema", {"type": _schema_types(_all.get(param, {}).get("type", ""))}),
                            ]
                        ),
                    )
                ]
            )
        )
    return _ret


def _get_schemas(settings, biothing_object, url):
    return OrderedDict(
        [
            (
                "string_or_array",
                {
                    "oneOf": [
                        {"type": "array", "items": {"type": "string"}},
                        {"type": "string"},
                    ]
                },
            ),
            (
                "int64_or_array",
                {
                    "oneOf": [
                        {"type": "array", "items": {"type": "integer", "format": "int64"}},
                        {"type": "integer", "format": "int64"},
                    ]
                },
            ),
            (
                "QueryResult",
                {
                    "type": "object",
                    "properties": OrderedDict(
                        [
                            ("max_score", {"type": "number", "format": "float"}),
                            ("took", {"type": "integer"}),
                            ("total", {"type": "integer"}),
                            (
                                "hits",
                                {
                                    "type": "array",
                                    "items": {"$ref": "#/components/schemas/{}".format(biothing_object.title())},
                                },
                            ),
                        ]
                    ),
                },
            ),
            (
                "QueryPOSTResult",
                {
                    "type": "array",
                    "items": {
                        "allOf": [
                            {"$ref": "#/components/schemas/{}".format(biothing_object.title())},
                            {
                                "type": "object",
                                "properties": {
                                    "_score": {"type": "number", "format": "float"},
                                    "query": {"type": "string"},
                                },
                            },
                        ]
                    },
                },
            ),
            (
                "ErrorResult",
                {"type": "object", "properties": {"success": {"type": "boolean"}, "message": {"type": "string"}}},
            ),
            (
                "{}".format(biothing_object.title()),
                {"type": "object", "required": ["_id"], "properties": {"_id": {"type": "string"}}},
            ),
        ]
    )


def _schema_types(t):
    # if t == type(bool()):
    if issubclass(t, bool):
        return "boolean"
    # if t == type(int()):
    if issubclass(t, int):
        return "integer"
    return "string"


def _string_types(t):
    # if t == type(str()):
    if issubclass(t, str):
        return " Type: string."
    # elif t == type(int()):
    elif issubclass(t, int):
        return " Type: integer."
    # elif t == type([]):
    elif issubclass(t, list):
        return " Type: string (list)."
    # elif t == type(bool()):
    elif issubclass(t, bool):
        return " Type: boolean."
    return " Type: string."


def _param_default_values(d):
    if not d:
        return " Default: None."
    # if type(d) == type(int()) or type(d) == type(str()):
    if isinstance(d, (int, str)):
        return " Default: {}.".format(d)
    # elif type(d) == type(True) and d:
    elif isinstance(d, bool) and d:
        return " Default: true."
    # elif type(d) == type(False) and not d:
    elif isinstance(d, bool) and not d:
        return " Default: false."
    return " Default: None."


def _param_max(m):
    if not m:
        return ""
    return " Max: {}.".format(m)


def _query_get_params(settings):
    _ret = {}
    for d in [
        settings.QUERY_GET_CONTROL_KWARGS,
        settings.QUERY_GET_ES_KWARGS,
        settings.QUERY_GET_ESQB_KWARGS,
        settings.QUERY_GET_TRANSFORM_KWARGS,
    ]:
        for k, v in d.items():
            if k != "q" and k in getattr(settings, "KWARG_DESCRIPTIONS", {}):
                _ret.setdefault(k, v)

    _ret["callback"] = {"type": str, "default": None}
    _ret["email"] = {"type": str, "default": None}

    return _ret


def _metadata_params(settings):
    _ret = {}
    for d in [
        settings.METADATA_GET_CONTROL_KWARGS,
        settings.METADATA_GET_ES_KWARGS,
        settings.METADATA_GET_ESQB_KWARGS,
        settings.METADATA_GET_TRANSFORM_KWARGS,
    ]:
        for k, v in d.items():
            if k in getattr(settings, "KWARG_DESCRIPTIONS", {}):
                _ret.setdefault(k, v)

    _ret["callback"] = {"type": str, "default": None}

    return _ret


def _annotation_get_params(settings):
    _ret = {}
    for d in [
        settings.ANNOTATION_GET_CONTROL_KWARGS,
        settings.ANNOTATION_GET_ES_KWARGS,
        settings.ANNOTATION_GET_ESQB_KWARGS,
        settings.ANNOTATION_GET_TRANSFORM_KWARGS,
    ]:
        for k, v in d.items():
            if k != "q" and k in getattr(settings, "KWARG_DESCRIPTIONS", {}):
                _ret.setdefault(k, v)

    _ret["callback"] = {"type": str, "default": None}
    _ret["email"] = {"type": str, "default": None}

    return _ret


def _query_post_params(settings):
    _ret = {}
    for d in [
        settings.QUERY_POST_CONTROL_KWARGS,
        settings.QUERY_POST_ES_KWARGS,
        settings.QUERY_POST_ESQB_KWARGS,
        settings.QUERY_POST_TRANSFORM_KWARGS,
    ]:
        for k, v in d.items():
            if k != "q" and k != "scopes" and k in getattr(settings, "KWARG_DESCRIPTIONS", {}):
                _ret.setdefault(k, v)

    _ret["email"] = {"type": str, "default": None}

    return _ret


def _annotation_post_params(settings):
    _ret = {}
    for d in [
        settings.ANNOTATION_POST_CONTROL_KWARGS,
        settings.ANNOTATION_POST_ES_KWARGS,
        settings.ANNOTATION_POST_ESQB_KWARGS,
        settings.ANNOTATION_POST_TRANSFORM_KWARGS,
    ]:
        for k, v in d.items():
            if k != "ids" and k in getattr(settings, "KWARG_DESCRIPTIONS", {}):
                _ret.setdefault(k, v)

    _ret["email"] = {"type": str, "default": None}

    return _ret


def _get_all_params(settings):
    # returns all possible parameters for a biothing web settings object
    if all_params:
        return all_params

    for d in [
        settings.ANNOTATION_GET_CONTROL_KWARGS,
        settings.ANNOTATION_GET_ES_KWARGS,
        settings.ANNOTATION_GET_ESQB_KWARGS,
        settings.ANNOTATION_GET_TRANSFORM_KWARGS,
        settings.ANNOTATION_POST_CONTROL_KWARGS,
        settings.ANNOTATION_POST_ES_KWARGS,
        settings.ANNOTATION_POST_ESQB_KWARGS,
        settings.ANNOTATION_POST_TRANSFORM_KWARGS,
        settings.QUERY_GET_CONTROL_KWARGS,
        settings.QUERY_GET_ES_KWARGS,
        settings.QUERY_GET_ESQB_KWARGS,
        settings.QUERY_GET_TRANSFORM_KWARGS,
        settings.QUERY_POST_CONTROL_KWARGS,
        settings.QUERY_POST_ES_KWARGS,
        settings.QUERY_POST_ESQB_KWARGS,
        settings.QUERY_POST_TRANSFORM_KWARGS,
        settings.METADATA_GET_CONTROL_KWARGS,
        settings.METADATA_GET_ES_KWARGS,
        settings.METADATA_GET_ESQB_KWARGS,
        settings.METADATA_GET_TRANSFORM_KWARGS,
    ]:
        for k, v in d.items():
            all_params.setdefault(k, v)

    return all_params


def _query_get_description(settings, biothing_object, url):
    _descriptions = getattr(settings, "KWARG_DESCRIPTIONS", {})
    _parameters = [
        OrderedDict(
            [
                ("name", "q"),
                ("in", "query"),
                ("description", _descriptions.get("q", {}).get("text_template").format(doc_query_syntax_url="")),
                ("required", True),
                ("example", getattr(settings, "STATUS_CHECK", {}).get("id", "EXAMPLE")),
                ("schema", {"type": "string"}),
            ]
        )
    ]
    for k in _query_get_params(settings).keys():
        _parameters.append(
            OrderedDict(
                [
                    ("name", _descriptions.get(k).get("name")),
                    ("$ref", "#/components/parameters/{}".format(_descriptions.get(k).get("name"))),
                ]
            )
        )
    return OrderedDict(
        [
            ("tags", ["query"]),
            (
                "summary",
                "Make {biothing_object} queries and return matching {biothing_object} hits. Supports JSONP and CORS as well.".format(
                    biothing_object=biothing_object
                ),
            ),
            ("parameters", _parameters),
            (
                "responses",
                OrderedDict(
                    [
                        (
                            "200",
                            OrderedDict(
                                [
                                    ("description", 'A query response with the "hits" property'),
                                    (
                                        "content",
                                        {"application/json": {"schema": {"$ref": "#/components/schemas/QueryResult"}}},
                                    ),
                                ]
                            ),
                        ),
                        (
                            "400",
                            OrderedDict(
                                [
                                    ("description", "A response indicating an improperly formatted query"),
                                    (
                                        "content",
                                        {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResult"}}},
                                    ),
                                ]
                            ),
                        ),
                    ]
                ),
            ),
        ]
    )


def _query_post_description(settings, biothing_object, url):
    _descriptions = getattr(settings, "KWARG_DESCRIPTIONS", {})
    _parameters = []
    for k in _query_post_params(settings).keys():
        _parameters.append(
            OrderedDict(
                [
                    ("name", _descriptions.get(k).get("name")),
                    ("$ref", "#/components/parameters/{}".format(_descriptions.get(k).get("name"))),
                ]
            )
        )
    return OrderedDict(
        [
            ("tags", ["query"]),
            (
                "summary",
                "Make {biothing_object} batch queries and return matching {biothing_object} hits".format(
                    biothing_object=biothing_object
                ),
            ),
            (
                "requestBody",
                {
                    "content": {
                        "application/x-www-form-urlencoded": {
                            "schema": {
                                "properties": OrderedDict(
                                    [
                                        (
                                            "q",
                                            OrderedDict(
                                                [
                                                    (
                                                        "description",
                                                        'multiple query terms separated by comma (also "+" or whitespace).  Does not support wildcard queries',
                                                    ),
                                                    ("type", "string"),
                                                ]
                                            ),
                                        ),
                                        (
                                            "scopes",
                                            OrderedDict(
                                                [
                                                    (
                                                        "description",
                                                        _descriptions.get("scopes", {})
                                                        .get("text_template", "")
                                                        .format(param_type="string", param_max=""),
                                                    ),
                                                    ("type", "string"),
                                                ]
                                            ),
                                        ),
                                    ]
                                )
                            }
                        }
                    }
                },
            ),
            ("parameters", _parameters),
            (
                "responses",
                OrderedDict(
                    [
                        (
                            "200",
                            OrderedDict(
                                [
                                    ("description", 'Query response objects with the "hits" property'),
                                    (
                                        "content",
                                        {
                                            "application/json": {
                                                "schema": {"$ref": "#/components/schemas/QueryPOSTResult"}
                                            }
                                        },
                                    ),
                                ]
                            ),
                        ),
                        (
                            "400",
                            OrderedDict(
                                [
                                    ("description", "A response indicating an improperly formatted query"),
                                    (
                                        "content",
                                        {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResult"}}},
                                    ),
                                ]
                            ),
                        ),
                    ]
                ),
            ),
        ]
    )


def _annotation_get_description(settings, biothing_object, url):
    _descriptions = getattr(settings, "KWARG_DESCRIPTIONS", {})
    _parameters = [
        OrderedDict(
            [
                ("name", "{biothing_object}id".format(biothing_object=biothing_object)),
                ("in", "path"),
                ("description", "{biothing_object} id".format(biothing_object=biothing_object)),
                ("required", True),
                ("example", getattr(settings, "STATUS_CHECK", {}).get("id", "EXAMPLE")),
                ("schema", {"type": "string"}),
            ]
        )
    ]
    for k in _annotation_get_params(settings).keys():
        _parameters.append(
            OrderedDict(
                [
                    ("name", _descriptions.get(k).get("name")),
                    ("$ref", "#/components/parameters/{}".format(_descriptions.get(k).get("name"))),
                ]
            )
        )
    return OrderedDict(
        [
            ("tags", ["{}".format(biothing_object)]),
            (
                "summary",
                "Retrieve {biothing_object} objects based on {biothing_object} id.  Supports JSONP and CORS as well.".format(
                    biothing_object=biothing_object
                ),
            ),
            ("parameters", _parameters),
            (
                "responses",
                OrderedDict(
                    [
                        (
                            "200",
                            OrderedDict(
                                [
                                    (
                                        "description",
                                        "A matching {biothing_object} object".format(biothing_object=biothing_object),
                                    ),
                                    (
                                        "content",
                                        {
                                            "application/json": {
                                                "schema": {
                                                    "$ref": "#/components/schemas/{}".format(biothing_object.title())
                                                }
                                            }
                                        },
                                    ),
                                ]
                            ),
                        ),
                        (
                            "404",
                            OrderedDict(
                                [
                                    (
                                        "description",
                                        "A response indicating an unknown {biothing_object} id".format(
                                            biothing_object=biothing_object
                                        ),
                                    )
                                ]
                            ),
                        ),
                    ]
                ),
            ),
        ]
    )


def _annotation_post_description(settings, biothing_object, url):
    _descriptions = getattr(settings, "KWARG_DESCRIPTIONS", {})
    _parameters = []
    for k in _annotation_post_params(settings).keys():
        _parameters.append(
            OrderedDict(
                [
                    ("name", _descriptions.get(k).get("name")),
                    ("$ref", "#/components/parameters/{}".format(_descriptions.get(k).get("name"))),
                ]
            )
        )
    return OrderedDict(
        [
            ("tags", ["{biothing_object}".format(biothing_object=biothing_object)]),
            (
                "summary",
                "For a list of {biothing_object} ids, return the matching {biothing_object} object".format(
                    biothing_object=biothing_object
                ),
            ),
            (
                "requestBody",
                {
                    "content": {
                        "application/x-www-form-urlencoded": {
                            "schema": OrderedDict(
                                [
                                    (
                                        "properties",
                                        {
                                            "ids": OrderedDict(
                                                [
                                                    (
                                                        "description",
                                                        _descriptions.get("ids", {})
                                                        .get("text_template", "")
                                                        .format(
                                                            biothing_object=biothing_object,
                                                            param_type=" Type: string (list).",
                                                            param_default_value="",
                                                            param_max=_param_max(
                                                                getattr(
                                                                    settings, "ANNOTATION_POST_CONTROL_KWARGS", {}
                                                                ).get("max", 1000)
                                                            ),
                                                        ),
                                                    ),
                                                    ("type", "string"),
                                                ]
                                            )
                                        },
                                    ),
                                    ("required", ["ids"]),
                                ]
                            )
                        }
                    }
                },
            ),
            ("parameters", _parameters),
            (
                "responses",
                OrderedDict(
                    [
                        (
                            "200",
                            OrderedDict(
                                [
                                    (
                                        "description",
                                        "A list of matching {biothing_object} objects".format(
                                            biothing_object=biothing_object
                                        ),
                                    ),
                                    (
                                        "content",
                                        {
                                            "application/json": {
                                                "schema": {
                                                    "type": "array",
                                                    "items": {
                                                        "$ref": "#/components/schemas/{}".format(
                                                            biothing_object.title()
                                                        )
                                                    },
                                                }
                                            }
                                        },
                                    ),
                                ]
                            ),
                        ),
                        (
                            "400",
                            OrderedDict(
                                [
                                    ("description", "A response indicating an improperly formatted query"),
                                    (
                                        "content",
                                        {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResult"}}},
                                    ),
                                ]
                            ),
                        ),
                    ]
                ),
            ),
        ]
    )


def _metadata_description(settings, biothing_object, url):
    return OrderedDict(
        [
            ("tags", ["metadata"]),
            ("summary", "Get metadata about the data available from {url}".format(url=url)),
            (
                "parameters",
                [OrderedDict([("name", "callback"), ("in", "query"), ("$ref", "#/components/parameters/callback")])],
            ),
            ("responses", {"200": {"description": "{url} metadata object".format(url=url)}}),
        ]
    )


def _metadata_fields_description(settings, biothing_object, url):
    _descriptions = getattr(settings, "KWARG_DESCRIPTIONS", {})
    _parameters = []
    for k in _metadata_params(settings).keys():
        _parameters.append(
            OrderedDict(
                [
                    ("name", _descriptions.get(k).get("name")),
                    ("$ref", "#/components/parameters/{}".format(_descriptions.get(k).get("name"))),
                ]
            )
        )
    return OrderedDict(
        [
            ("tags", ["metadata"]),
            (
                "summary",
                "Get metadata about the data fields available from a {url} {biothing_object} object".format(
                    biothing_object=biothing_object, url=url
                ),
            ),
            ("parameters", _parameters),
            ("responses", {"200": {"description": "{url} metadata fields object".format(url=url)}}),
        ]
    )


def create_openapi_json(_settings, **kwargs):
    _url = getattr(_settings, "GA_TRACKER_URL", "MyBiothing.info")
    _biothing_object = getattr(_settings, "ES_DOC_TYPE", "biothing")
    _doc = OrderedDict(
        [
            ("openapi", kwargs.get("openapi", "3.0.0")),
            (
                "info",
                OrderedDict(
                    [
                        (
                            "version",
                            kwargs.get(
                                "info__version",
                                "{:.1f}".format(float(getattr(_settings, "API_VERSION", "v1").lstrip("v"))),
                            ),
                        ),
                        (
                            "title",
                            kwargs.get(
                                "info__title", "{} API".format(getattr(_settings, "GA_TRACKER_URL", "MyBiothing.info"))
                            ),
                        ),
                        (
                            "description",
                            kwargs.get(
                                "info__description",
                                getattr(_settings, "KWARG_DESCRIPTION", {}).get(
                                    "_root",
                                    "Documentation of the {url} {biothing_object} query web services.  Learn more about [{url}](http://{url}/)".format(
                                        url=_url, biothing_object=_biothing_object
                                    ),
                                ),
                            ),
                        ),
                        ("termsOfService", kwargs.get("info__termsOfService", "http://{url}/terms".format(url=_url))),
                        (
                            "contact",
                            OrderedDict(
                                [
                                    ("name", kwargs.get("info__contact__name", "Chunlei Wu")),
                                    ("x-role", kwargs.get("info__contact__x_role", "responsible developer")),
                                    ("email", kwargs.get("info__contact__email", "help@biothings.io")),
                                    ("x-id", kwargs.get("info__contact__x_id", "https://github.com/newgene")),
                                ]
                            ),
                        ),
                    ]
                ),
            ),
            (
                "servers",
                [
                    OrderedDict(
                        [
                            (
                                "url",
                                "http://{url}/{version}".format(
                                    url=_url, version=getattr(_settings, "API_VERSION", "v1")
                                ),
                            ),
                            ("description", "Production server"),
                        ]
                    ),
                    OrderedDict(
                        [
                            (
                                "url",
                                "https://{url}/{version}".format(
                                    url=_url, version=getattr(_settings, "API_VERSION", "v1")
                                ),
                            ),
                            ("description", "Encrypted Production server"),
                        ]
                    ),
                ],
            ),
            (
                "tags",
                [
                    {"name": "{biothing_object}".format(biothing_object=_biothing_object)},
                    {"name": "query"},
                    {"name": "metadata"},
                ],
            ),
            (
                "paths",
                OrderedDict(
                    [
                        (
                            "/{biothing_object}/{{{biothing_object}id}}".format(biothing_object=_biothing_object),
                            # annotation GET
                            OrderedDict(
                                [
                                    (
                                        "get",
                                        _annotation_get_description(
                                            settings=_settings, biothing_object=_biothing_object, url=_url
                                        ),
                                    )
                                ]
                            ),
                        ),
                        # annotation POST
                        (
                            "/{biothing_object}".format(biothing_object=_biothing_object),
                            OrderedDict(
                                [
                                    (
                                        "post",
                                        _annotation_post_description(
                                            settings=_settings, biothing_object=_biothing_object, url=_url
                                        ),
                                    )
                                ]
                            ),
                        ),
                        # query GET/POST
                        (
                            "/query",
                            OrderedDict(
                                [
                                    (
                                        "get",
                                        _query_get_description(
                                            settings=_settings, biothing_object=_biothing_object, url=_url
                                        ),
                                    ),
                                    (
                                        "post",
                                        _query_post_description(
                                            settings=_settings, biothing_object=_biothing_object, url=_url
                                        ),
                                    ),
                                ]
                            ),
                        ),
                        # metadata GET
                        (
                            "/metadata",
                            OrderedDict(
                                [
                                    (
                                        "get",
                                        _metadata_description(
                                            settings=_settings, biothing_object=_biothing_object, url=_url
                                        ),
                                    )
                                ]
                            ),
                        ),
                        # metadata/fields GET
                        (
                            "/metadata/fields",
                            OrderedDict(
                                [
                                    (
                                        "get",
                                        _metadata_fields_description(
                                            settings=_settings, biothing_object=_biothing_object, url=_url
                                        ),
                                    )
                                ]
                            ),
                        ),
                    ]
                ),
            ),
            (
                "components",
                OrderedDict(
                    [
                        (
                            "parameters",
                            _get_common_parameters(settings=_settings, biothing_object=_biothing_object, url=_url),
                        ),
                        ("schemas", _get_schemas(settings=_settings, biothing_object=_biothing_object, url=_url)),
                    ]
                ),
            ),
        ]
    )
    return _doc


def create_openapi_yaml(settings, yaml_file, **kwargs):
    """Create an openapi compliant spec for the biothing project whose settings are input.

    settings - a biothings web settings object
    yaml_file - the output file
    """
    setup_yaml()
    _json = create_openapi_json(settings, **kwargs)
    with open(yaml_file, "w") as outf:
        yaml.dump(_json, outf, default_flow_style=False)

92e94272c1a41941de542bb0f70923e157ce7708
<mask token>


def _get_common_parameters(settings, biothing_object, url):
    _all = _get_all_params(settings)
    _ret = OrderedDict([])
    _common_params_list = [(k, v) for k, v in _all.items() if k != 'q' and 
        k != 'scopes' and k != 'ids' and k in getattr(settings,
        'KWARG_DESCRIPTIONS', {})]
    _common_params_list.extend([('callback', {'type': str, 'default': None}
        ), ('email', {'type': str, 'default': None})])
    _description_templates = getattr(settings, 'KWARG_DESCRIPTIONS', {})
    for param, _description in _common_params_list:
        _description_template_object = _description_templates.get(param)
        _param_display = _description_template_object.get('name')
        _ret.update(OrderedDict([(_param_display, OrderedDict([('name',
            _param_display), ('in', 'query'), ('description',
            _description_template_object.get('text_template', '').format(
            biothing_object=biothing_object, param_type=_string_types(_all.
            get(param, {}).get('type', '')), param_default_value=
            _param_default_values(_all.get(param, {}).get('default', '')),
            param_max=_param_max(_all.get(param, {}).get('max', '')))), (
            'schema', {'type': _schema_types(_all.get(param, {}).get('type',
            ''))})]))]))
    return _ret


<mask token>


def _query_get_params(settings):
    _ret = {}
    for d in [settings.QUERY_GET_CONTROL_KWARGS, settings.
        QUERY_GET_ES_KWARGS, settings.QUERY_GET_ESQB_KWARGS, settings.
        QUERY_GET_TRANSFORM_KWARGS]:
        for k, v in d.items():
            if k != 'q' and k in getattr(settings, 'KWARG_DESCRIPTIONS', {}):
                _ret.setdefault(k, v)
    _ret['callback'] = {'type': str, 'default': None}
    _ret['email'] = {'type': str, 'default': None}
    return _ret


<mask token>


def _annotation_get_params(settings):
    _ret = {}
    for d in [settings.ANNOTATION_GET_CONTROL_KWARGS, settings.
        ANNOTATION_GET_ES_KWARGS, settings.ANNOTATION_GET_ESQB_KWARGS,
        settings.ANNOTATION_GET_TRANSFORM_KWARGS]:
        for k, v in d.items():
            if k != 'q' and k in getattr(settings, 'KWARG_DESCRIPTIONS', {}):
                _ret.setdefault(k, v)
    _ret['callback'] = {'type': str, 'default': None}
    _ret['email'] = {'type': str, 'default': None}
    return _ret


<mask token>


def _annotation_get_description(settings, biothing_object, url):
    _descriptions = getattr(settings, 'KWARG_DESCRIPTIONS', {})
    _parameters = [OrderedDict([('name', '{biothing_object}id'.format(
        biothing_object=biothing_object)), ('in', 'path'), ('description',
        '{biothing_object} id'.format(biothing_object=biothing_object)), (
        'required', True), ('example', getattr(settings, 'STATUS_CHECK', {}
        ).get('id', 'EXAMPLE')), ('schema', {'type': 'string'})])]
    for k in _annotation_get_params(settings).keys():
        _parameters.append(OrderedDict([('name', _descriptions.get(k).get(
            'name')), ('$ref', '#/components/parameters/{}'.format(
            _descriptions.get(k).get('name')))]))
    return OrderedDict([('tags', ['{}'.format(biothing_object)]), (
        'summary',
        'Retrieve {biothing_object} objects based on {biothing_object} id.  Supports JSONP and CORS as well.'
        .format(biothing_object=biothing_object)), ('parameters',
        _parameters), ('responses', OrderedDict([('200', OrderedDict([(
        'description', 'A matching {biothing_object} object'.format(
        biothing_object=biothing_object)), ('content', {'application/json':
        {'schema': {'$ref': '#/components/schemas/{}'.format(
        biothing_object.title())}}})])), ('404', OrderedDict([(
        'description',
        'A response indicating an unknown {biothing_object} id'.format(
        biothing_object=biothing_object))]))]))])


<mask token>


def _metadata_description(settings, biothing_object, url):
    return OrderedDict([('tags', ['metadata']), ('summary',
        'Get metadata about the data available from {url}'.format(url=url)),
        ('parameters', [OrderedDict([('name', 'callback'), ('in', 'query'),
        ('$ref', '#/components/parameters/callback')])]), ('responses', {
        '200': {'description': '{url} metadata object'.format(url=url)}})])


def _metadata_fields_description(settings, biothing_object, url):
    _descriptions = getattr(settings, 'KWARG_DESCRIPTIONS', {})
    _parameters = []
    for k in _metadata_params(settings).keys():
        _parameters.append(OrderedDict([('name', _descriptions.get(k).get(
            'name')), ('$ref', '#/components/parameters/{}'.format(
            _descriptions.get(k).get('name')))]))
    return OrderedDict([('tags', ['metadata']), ('summary',
        'Get metadata about the data fields available from a {url} {biothing_object} object'
        .format(biothing_object=biothing_object, url=url)), ('parameters',
        _parameters), ('responses', {'200': {'description':
        '{url} metadata fields object'.format(url=url)}})])


def create_openapi_json(_settings, **kwargs):
    _url = getattr(_settings, 'GA_TRACKER_URL', 'MyBiothing.info')
    _biothing_object = getattr(_settings, 'ES_DOC_TYPE', 'biothing')
    _doc = OrderedDict([('openapi', kwargs.get('openapi', '3.0.0')), (
        'info', OrderedDict([('version', kwargs.get('info__version',
        '{:.1f}'.format(float(getattr(_settings, 'API_VERSION', 'v1').
        lstrip('v'))))), ('title', kwargs.get('info__title', '{} API'.
        format(getattr(_settings, 'GA_TRACKER_URL', 'MyBiothing.info')))),
        ('description', kwargs.get('info__description', getattr(_settings,
        'KWARG_DESCRIPTION', {}).get('_root',
        'Documentation of the {url} {biothing_object} query web services.  Learn more about [{url}](http://{url}/)'
        .format(url=_url, biothing_object=_biothing_object)))), (
        'termsOfService', kwargs.get('info__termsOfService',
        'http://{url}/terms'.format(url=_url))), ('contact', OrderedDict([(
        'name', kwargs.get('info__contact__name', 'Chunlei Wu')), ('x-role',
        kwargs.get('info__contact__x_role', 'responsible developer')), (
        'email', kwargs.get('info__contact__email', 'help@biothings.io')),
        ('x-id', kwargs.get('info__contact__x_id',
        'https://github.com/newgene'))]))])), ('servers', [OrderedDict([(
        'url', 'http://{url}/{version}'.format(url=_url, version=getattr(
        _settings, 'API_VERSION', 'v1'))), ('description',
        'Production server')]), OrderedDict([('url',
        'https://{url}/{version}'.format(url=_url, version=getattr(
        _settings, 'API_VERSION', 'v1'))), ('description',
        'Encrypted Production server')])]), ('tags', [{'name':
        '{biothing_object}'.format(biothing_object=_biothing_object)}, {
        'name': 'query'}, {'name': 'metadata'}]), ('paths', OrderedDict([(
        '/{biothing_object}/{{{biothing_object}id}}'.format(biothing_object
        =_biothing_object), OrderedDict([('get',
        _annotation_get_description(settings=_settings, biothing_object=
        _biothing_object, url=_url))])), ('/{biothing_object}'.format(
        biothing_object=_biothing_object), OrderedDict([('post',
        _annotation_post_description(settings=_settings, biothing_object=
        _biothing_object, url=_url))])), ('/query', OrderedDict([('get',
        _query_get_description(settings=_settings, biothing_object=
        _biothing_object, url=_url)), ('post', _query_post_description(
        settings=_settings, biothing_object=_biothing_object, url=_url))])),
        ('/metadata', OrderedDict([('get', _metadata_description(settings=
        _settings, biothing_object=_biothing_object, url=_url))])), (
        '/metadata/fields', OrderedDict([('get',
        _metadata_fields_description(settings=_settings, biothing_object=
        _biothing_object, url=_url))]))])), ('components', OrderedDict([(
        'parameters', _get_common_parameters(settings=_settings,
        biothing_object=_biothing_object, url=_url)), ('schemas',
        _get_schemas(settings=_settings, biothing_object=_biothing_object,
        url=_url))]))])
    return _doc


def create_openapi_yaml(settings, yaml_file, **kwargs):
    """Create an openapi compliant spec for the biothing project whose settings are input.

    settings - a biothings web settings object
    yaml_file - the output file
    """
    setup_yaml()
    _json = create_openapi_json(settings, **kwargs)
    with open(yaml_file, 'w') as outf:
        yaml.dump(_json, outf, default_flow_style=False)

<mask token>


def setup_yaml():
    """https://stackoverflow.com/a/8661021"""
    represent_dict_order = lambda self, data: self.represent_mapping(
        'tag:yaml.org,2002:map', data.items())
    yaml.add_representer(OrderedDict, represent_dict_order)


<mask token>


def _get_common_parameters(settings, biothing_object, url):
    _all = _get_all_params(settings)
    _ret = OrderedDict([])
    _common_params_list = [(k, v) for k, v in _all.items() if k != 'q' and 
        k != 'scopes' and k != 'ids' and k in getattr(settings,
        'KWARG_DESCRIPTIONS', {})]
    _common_params_list.extend([('callback', {'type': str, 'default': None}
        ), ('email', {'type': str, 'default': None})])
    _description_templates = getattr(settings, 'KWARG_DESCRIPTIONS', {})
    for param, _description in _common_params_list:
        _description_template_object = _description_templates.get(param)
        _param_display = _description_template_object.get('name')
        _ret.update(OrderedDict([(_param_display, OrderedDict([('name',
            _param_display), ('in', 'query'), ('description',
            _description_template_object.get('text_template', '').format(
            biothing_object=biothing_object, param_type=_string_types(_all.
            get(param, {}).get('type', '')), param_default_value=
            _param_default_values(_all.get(param, {}).get('default', '')),
            param_max=_param_max(_all.get(param, {}).get('max', '')))), (
            'schema', {'type': _schema_types(_all.get(param, {}).get('type',
            ''))})]))]))
    return _ret


def _get_schemas(settings, biothing_object, url):
    return OrderedDict([('string_or_array', {'oneOf': [{'type': 'array',
        'items': {'type': 'string'}}, {'type': 'string'}]}), (
        'int64_or_array', {'oneOf': [{'type': 'array', 'items': {'type':
        'integer', 'format': 'int64'}}, {'type': 'integer', 'format':
        'int64'}]}), ('QueryResult', {'type': 'object', 'properties':
        OrderedDict([('max_score', {'type': 'number', 'format': 'float'}),
        ('took', {'type': 'integer'}), ('total', {'type': 'integer'}), (
        'hits', {'type': 'array', 'items': {'$ref':
        '#/components/schemas/{}'.format(biothing_object.title())}})])}), (
        'QueryPOSTResult', {'type': 'array', 'items': {'allOf': [{'$ref':
        '#/components/schemas/{}'.format(biothing_object.title())}, {'type':
        'object', 'properties': {'_score': {'type': 'number', 'format':
        'float'}, 'query': {'type': 'string'}}}]}}), ('ErrorResult', {
        'type': 'object', 'properties': {'success': {'type': 'boolean'},
        'message': {'type': 'string'}}}), ('{}'.format(biothing_object.
        title()), {'type': 'object', 'required': ['_id'], 'properties': {
        '_id': {'type': 'string'}}})])


<mask token>


def _param_default_values(d):
    if not d:
        return ' Default: None.'
    if isinstance(d, (int, str)):
        return ' Default: {}.'.format(d)
    elif isinstance(d, bool) and d:
        return ' Default: true.'
    elif isinstance(d, bool) and not d:
        return ' Default: false.'
    return ' Default: None.'


def _param_max(m):
    if not m:
        return ''
    return ' Max: {}.'.format(m)


def _query_get_params(settings):
    _ret = {}
    for d in [settings.QUERY_GET_CONTROL_KWARGS, settings.
        QUERY_GET_ES_KWARGS, settings.QUERY_GET_ESQB_KWARGS, settings.
        QUERY_GET_TRANSFORM_KWARGS]:
        for k, v in d.items():
            if k != 'q' and k in getattr(settings, 'KWARG_DESCRIPTIONS', {}):
                _ret.setdefault(k, v)
    _ret['callback'] = {'type': str, 'default': None}
    _ret['email'] = {'type': str, 'default': None}
    return _ret


def _metadata_params(settings):
    _ret = {}
    for d in [settings.METADATA_GET_CONTROL_KWARGS, settings.
        METADATA_GET_ES_KWARGS, settings.METADATA_GET_ESQB_KWARGS, settings
        .METADATA_GET_TRANSFORM_KWARGS]:
        for k, v in d.items():
            if k in getattr(settings, 'KWARG_DESCRIPTIONS', {}):
                _ret.setdefault(k, v)
    _ret['callback'] = {'type': str, 'default': None}
    return _ret


def _annotation_get_params(settings):
    _ret = {}
    for d in [settings.ANNOTATION_GET_CONTROL_KWARGS, settings.
        ANNOTATION_GET_ES_KWARGS, settings.ANNOTATION_GET_ESQB_KWARGS,
        settings.ANNOTATION_GET_TRANSFORM_KWARGS]:
        for k, v in d.items():
            if k != 'q' and k in getattr(settings, 'KWARG_DESCRIPTIONS', {}):
                _ret.setdefault(k, v)
    _ret['callback'] = {'type': str, 'default': None}
    _ret['email'] = {'type': str, 'default': None}
    return _ret


def _query_post_params(settings):
    _ret = {}
    for d in [settings.QUERY_POST_CONTROL_KWARGS, settings.
        QUERY_POST_ES_KWARGS, settings.QUERY_POST_ESQB_KWARGS, settings.
        QUERY_POST_TRANSFORM_KWARGS]:
        for k, v in d.items():
            if k != 'q' and k != 'scopes' and k in getattr(settings,
                'KWARG_DESCRIPTIONS', {}):
                _ret.setdefault(k, v)
    _ret['email'] = {'type': str, 'default': None}
    return _ret


def _annotation_post_params(settings):
    _ret = {}
    for d in [settings.ANNOTATION_POST_CONTROL_KWARGS, settings.
        ANNOTATION_POST_ES_KWARGS, settings.ANNOTATION_POST_ESQB_KWARGS,
        settings.ANNOTATION_POST_TRANSFORM_KWARGS]:
        for k, v in d.items():
            if k != 'ids' and k in getattr(settings, 'KWARG_DESCRIPTIONS', {}):
                _ret.setdefault(k, v)
    _ret['email'] = {'type': str, 'default': None}
    return _ret


def _get_all_params(settings):
    if all_params:
        return all_params
    for d in [settings.ANNOTATION_GET_CONTROL_KWARGS, settings.
        ANNOTATION_GET_ES_KWARGS, settings.ANNOTATION_GET_ESQB_KWARGS,
        settings.ANNOTATION_GET_TRANSFORM_KWARGS, settings.
        ANNOTATION_POST_CONTROL_KWARGS, settings.ANNOTATION_POST_ES_KWARGS,
        settings.ANNOTATION_POST_ESQB_KWARGS, settings.
        ANNOTATION_POST_TRANSFORM_KWARGS, settings.QUERY_GET_CONTROL_KWARGS,
        settings.QUERY_GET_ES_KWARGS, settings.QUERY_GET_ESQB_KWARGS,
        settings.QUERY_GET_TRANSFORM_KWARGS, settings.
        QUERY_POST_CONTROL_KWARGS, settings.QUERY_POST_ES_KWARGS, settings.
        QUERY_POST_ESQB_KWARGS, settings.QUERY_POST_TRANSFORM_KWARGS,
        settings.METADATA_GET_CONTROL_KWARGS, settings.
        METADATA_GET_ES_KWARGS, settings.METADATA_GET_ESQB_KWARGS, settings
        .METADATA_GET_TRANSFORM_KWARGS]:
        for k, v in d.items():
            all_params.setdefault(k, v)
    return all_params


def _query_get_description(settings, biothing_object, url):
    _descriptions = getattr(settings, 'KWARG_DESCRIPTIONS', {})
    _parameters = [OrderedDict([('name', 'q'), ('in', 'query'), (
        'description', _descriptions.get('q', {}).get('text_template').
        format(doc_query_syntax_url='')), ('required', True), ('example',
        getattr(settings, 'STATUS_CHECK', {}).get('id', 'EXAMPLE')), (
        'schema', {'type': 'string'})])]
    for k in _query_get_params(settings).keys():
        _parameters.append(OrderedDict([('name', _descriptions.get(k).get(
            'name')), ('$ref', '#/components/parameters/{}'.format(
            _descriptions.get(k).get('name')))]))
    return OrderedDict([('tags', ['query']), ('summary',
        'Make {biothing_object} queries and return matching {biothing_object} hits. Supports JSONP and CORS as well.'
        .format(biothing_object=biothing_object)), ('parameters',
        _parameters), ('responses', OrderedDict([('200', OrderedDict([(
        'description', 'A query response with the "hits" property'), (
        'content', {'application/json': {'schema': {'$ref':
        '#/components/schemas/QueryResult'}}})])), ('400', OrderedDict([(
        'description',
        'A response indicating an improperly formatted query'), ('content',
        {'application/json': {'schema': {'$ref':
        '#/components/schemas/ErrorResult'}}})]))]))])


<mask token>


def _annotation_get_description(settings, biothing_object, url):
    _descriptions = getattr(settings, 'KWARG_DESCRIPTIONS', {})
    _parameters = [OrderedDict([('name', '{biothing_object}id'.format(
        biothing_object=biothing_object)), ('in', 'path'), ('description',
        '{biothing_object} id'.format(biothing_object=biothing_object)), (
        'required', True), ('example', getattr(settings, 'STATUS_CHECK', {}
        ).get('id', 'EXAMPLE')), ('schema', {'type': 'string'})])]
    for k in _annotation_get_params(settings).keys():
        _parameters.append(OrderedDict([('name', _descriptions.get(k).get(
            'name')), ('$ref', '#/components/parameters/{}'.format(
            _descriptions.get(k).get('name')))]))
    return OrderedDict([('tags', ['{}'.format(biothing_object)]), (
        'summary',
        'Retrieve {biothing_object} objects based on {biothing_object} id.  Supports JSONP and CORS as well.'
        .format(biothing_object=biothing_object)), ('parameters',
        _parameters), ('responses', OrderedDict([('200', OrderedDict([(
        'description', 'A matching {biothing_object} object'.format(
        biothing_object=biothing_object)), ('content', {'application/json':
        {'schema': {'$ref': '#/components/schemas/{}'.format(
        biothing_object.title())}}})])), ('404', OrderedDict([(
        'description',
        'A response indicating an unknown {biothing_object} id'.format(
        biothing_object=biothing_object))]))]))])


<mask token>


def _metadata_description(settings, biothing_object, url):
    return OrderedDict([('tags', ['metadata']), ('summary',
        'Get metadata about the data available from {url}'.format(url=url)),
        ('parameters', [OrderedDict([('name', 'callback'), ('in', 'query'),
        ('$ref', '#/components/parameters/callback')])]), ('responses', {
        '200': {'description': '{url} metadata object'.format(url=url)}})])


def _metadata_fields_description(settings, biothing_object, url):
    _descriptions = getattr(settings, 'KWARG_DESCRIPTIONS', {})
    _parameters = []
    for k in _metadata_params(settings).keys():
        _parameters.append(OrderedDict([('name', _descriptions.get(k).get(
            'name')), ('$ref', '#/components/parameters/{}'.format(
            _descriptions.get(k).get('name')))]))
    return OrderedDict([('tags', ['metadata']), ('summary',
        'Get metadata about the data fields available from a {url} {biothing_object} object'
        .format(biothing_object=biothing_object, url=url)), ('parameters',
        _parameters), ('responses', {'200': {'description':
        '{url} metadata fields object'.format(url=url)}})])


def create_openapi_json(_settings, **kwargs):
    _url = getattr(_settings, 'GA_TRACKER_URL', 'MyBiothing.info')
    _biothing_object = getattr(_settings, 'ES_DOC_TYPE', 'biothing')
    _doc = OrderedDict([('openapi', kwargs.get('openapi', '3.0.0')), (
        'info', OrderedDict([('version', kwargs.get('info__version',
        '{:.1f}'.format(float(getattr(_settings, 'API_VERSION', 'v1').
        lstrip('v'))))), ('title', kwargs.get('info__title', '{} API'.
        format(getattr(_settings, 'GA_TRACKER_URL', 'MyBiothing.info')))),
        ('description', kwargs.get('info__description', getattr(_settings,
        'KWARG_DESCRIPTION', {}).get('_root',
        'Documentation of the {url} {biothing_object} query web services.  Learn more about [{url}](http://{url}/)'
        .format(url=_url, biothing_object=_biothing_object)))), (
        'termsOfService', kwargs.get('info__termsOfService',
        'http://{url}/terms'.format(url=_url))), ('contact', OrderedDict([(
        'name', kwargs.get('info__contact__name', 'Chunlei Wu')), ('x-role',
        kwargs.get('info__contact__x_role', 'responsible developer')), (
        'email', kwargs.get('info__contact__email', 'help@biothings.io')),
        ('x-id', kwargs.get('info__contact__x_id',
        'https://github.com/newgene'))]))])), ('servers', [OrderedDict([(
        'url', 'http://{url}/{version}'.format(url=_url, version=getattr(
        _settings, 'API_VERSION', 'v1'))), ('description',
        'Production server')]), OrderedDict([('url',
        'https://{url}/{version}'.format(url=_url, version=getattr(
        _settings, 'API_VERSION', 'v1'))), ('description',
        'Encrypted Production server')])]), ('tags', [{'name':
        '{biothing_object}'.format(biothing_object=_biothing_object)}, {
        'name': 'query'}, {'name': 'metadata'}]), ('paths', OrderedDict([(
        '/{biothing_object}/{{{biothing_object}id}}'.format(biothing_object
        =_biothing_object), OrderedDict([('get',
        _annotation_get_description(settings=_settings, biothing_object=
        _biothing_object, url=_url))])), ('/{biothing_object}'.format(
        biothing_object=_biothing_object), OrderedDict([('post',
        _annotation_post_description(settings=_settings, biothing_object=
        _biothing_object, url=_url))])), ('/query', OrderedDict([('get',
        _query_get_description(settings=_settings, biothing_object=
        _biothing_object, url=_url)), ('post', _query_post_description(
        settings=_settings, biothing_object=_biothing_object, url=_url))])),
        ('/metadata', OrderedDict([('get', _metadata_description(settings=
        _settings, biothing_object=_biothing_object, url=_url))])), (
        '/metadata/fields', OrderedDict([('get',
        _metadata_fields_description(settings=_settings, biothing_object=
        _biothing_object, url=_url))]))])), ('components', OrderedDict([(
        'parameters', _get_common_parameters(settings=_settings,
        biothing_object=_biothing_object, url=_url)), ('schemas',
        _get_schemas(settings=_settings, biothing_object=_biothing_object,
        url=_url))]))])
    return _doc


def create_openapi_yaml(settings, yaml_file, **kwargs):
    """Create an openapi compliant spec for the biothing project whose settings are input.

    settings - a biothings web settings object
    yaml_file - the output file
    """
    setup_yaml()
    _json = create_openapi_json(settings, **kwargs)
    with open(yaml_file, 'w') as outf:
        yaml.dump(_json, outf, default_flow_style=False)

<mask token>


def setup_yaml():
    """https://stackoverflow.com/a/8661021"""
    represent_dict_order = lambda self, data: self.represent_mapping(
        'tag:yaml.org,2002:map', data.items())
    yaml.add_representer(OrderedDict, represent_dict_order)


<mask token>


def _get_common_parameters(settings, biothing_object, url):
    _all = _get_all_params(settings)
    _ret = OrderedDict([])
    _common_params_list = [(k, v) for k, v in _all.items() if k != 'q' and 
        k != 'scopes' and k != 'ids' and k in getattr(settings,
        'KWARG_DESCRIPTIONS', {})]
    _common_params_list.extend([('callback', {'type': str, 'default': None}
        ), ('email', {'type': str, 'default': None})])
    _description_templates = getattr(settings, 'KWARG_DESCRIPTIONS', {})
    for param, _description in _common_params_list:
        _description_template_object = _description_templates.get(param)
        _param_display = _description_template_object.get('name')
        _ret.update(OrderedDict([(_param_display, OrderedDict([('name',
            _param_display), ('in', 'query'), ('description',
            _description_template_object.get('text_template', '').format(
            biothing_object=biothing_object, param_type=_string_types(_all.
            get(param, {}).get('type', '')), param_default_value=
            _param_default_values(_all.get(param, {}).get('default', '')),
            param_max=_param_max(_all.get(param, {}).get('max', '')))), (
            'schema', {'type': _schema_types(_all.get(param, {}).get('type',
            ''))})]))]))
    return _ret


def _get_schemas(settings, biothing_object, url):
    return OrderedDict([('string_or_array', {'oneOf': [{'type': 'array',
        'items': {'type': 'string'}}, {'type': 'string'}]}), (
        'int64_or_array', {'oneOf': [{'type': 'array', 'items': {'type':
        'integer', 'format': 'int64'}}, {'type': 'integer', 'format':
        'int64'}]}), ('QueryResult', {'type': 'object', 'properties':
        OrderedDict([('max_score', {'type': 'number', 'format': 'float'}),
        ('took', {'type': 'integer'}), ('total', {'type': 'integer'}), (
        'hits', {'type': 'array', 'items': {'$ref':
        '#/components/schemas/{}'.format(biothing_object.title())}})])}), (
        'QueryPOSTResult', {'type': 'array', 'items': {'allOf': [{'$ref':
        '#/components/schemas/{}'.format(biothing_object.title())}, {'type':
        'object', 'properties': {'_score': {'type': 'number', 'format':
        'float'}, 'query': {'type': 'string'}}}]}}), ('ErrorResult', {
        'type': 'object', 'properties': {'success': {'type': 'boolean'},
        'message': {'type': 'string'}}}), ('{}'.format(biothing_object.
        title()), {'type': 'object', 'required': ['_id'], 'properties': {
        '_id': {'type': 'string'}}})])


def _schema_types(t):
    if issubclass(t, bool):
        return 'boolean'
    if issubclass(t, int):
        return 'integer'
    return 'string'


def _string_types(t):
    if issubclass(t, str):
        return ' Type: string.'
    elif issubclass(t, int):
        return ' Type: integer.'
    elif issubclass(t, list):
        return ' Type: string (list).'
    elif issubclass(t, bool):
        return ' Type: boolean.'
    return ' Type: string.'


def _param_default_values(d):
    if not d:
        return ' Default: None.'
    if isinstance(d, (int, str)):
        return ' Default: {}.'.format(d)
    elif isinstance(d, bool) and d:
        return ' Default: true.'
    elif isinstance(d, bool) and not d:
        return ' Default: false.'
    return ' Default: None.'


def _param_max(m):
    if not m:
        return ''
    return ' Max: {}.'.format(m)


def _query_get_params(settings):
    _ret = {}
    for d in [settings.QUERY_GET_CONTROL_KWARGS, settings.
        QUERY_GET_ES_KWARGS, settings.QUERY_GET_ESQB_KWARGS, settings.
        QUERY_GET_TRANSFORM_KWARGS]:
        for k, v in d.items():
            if k != 'q' and k in getattr(settings, 'KWARG_DESCRIPTIONS', {}):
                _ret.setdefault(k, v)
    _ret['callback'] = {'type': str, 'default': None}
    _ret['email'] = {'type': str, 'default': None}
    return _ret


def _metadata_params(settings):
    _ret = {}
    for d in [settings.METADATA_GET_CONTROL_KWARGS, settings.
        METADATA_GET_ES_KWARGS, settings.METADATA_GET_ESQB_KWARGS, settings
        .METADATA_GET_TRANSFORM_KWARGS]:
        for k, v in d.items():
            if k in getattr(settings, 'KWARG_DESCRIPTIONS', {}):
                _ret.setdefault(k, v)
    _ret['callback'] = {'type': str, 'default': None}
    return _ret


def _annotation_get_params(settings):
    _ret = {}
    for d in [settings.ANNOTATION_GET_CONTROL_KWARGS, settings.
        ANNOTATION_GET_ES_KWARGS, settings.ANNOTATION_GET_ESQB_KWARGS,
        settings.ANNOTATION_GET_TRANSFORM_KWARGS]:
        for k, v in d.items():
            if k != 'q' and k in getattr(settings, 'KWARG_DESCRIPTIONS', {}):
                _ret.setdefault(k, v)
    _ret['callback'] = {'type': str, 'default': None}
    _ret['email'] = {'type': str, 'default': None}
    return _ret


def _query_post_params(settings):
    _ret = {}
    for d in [settings.QUERY_POST_CONTROL_KWARGS, settings.
        QUERY_POST_ES_KWARGS, settings.QUERY_POST_ESQB_KWARGS, settings.
        QUERY_POST_TRANSFORM_KWARGS]:
        for k, v in d.items():
            if k != 'q' and k != 'scopes' and k in getattr(settings,
                'KWARG_DESCRIPTIONS', {}):
                _ret.setdefault(k, v)
    _ret['email'] = {'type': str, 'default': None}
    return _ret


def _annotation_post_params(settings):
    _ret = {}
    for d in [settings.ANNOTATION_POST_CONTROL_KWARGS, settings.
        ANNOTATION_POST_ES_KWARGS, settings.ANNOTATION_POST_ESQB_KWARGS,
        settings.ANNOTATION_POST_TRANSFORM_KWARGS]:
        for k, v in d.items():
            if k != 'ids' and k in getattr(settings, 'KWARG_DESCRIPTIONS', {}):
                _ret.setdefault(k, v)
    _ret['email'] = {'type': str, 'default': None}
    return _ret


def _get_all_params(settings):
    if all_params:
        return all_params
    for d in [settings.ANNOTATION_GET_CONTROL_KWARGS, settings.
        ANNOTATION_GET_ES_KWARGS, settings.ANNOTATION_GET_ESQB_KWARGS,
        settings.ANNOTATION_GET_TRANSFORM_KWARGS, settings.
        ANNOTATION_POST_CONTROL_KWARGS, settings.ANNOTATION_POST_ES_KWARGS,
        settings.ANNOTATION_POST_ESQB_KWARGS, settings.
        ANNOTATION_POST_TRANSFORM_KWARGS, settings.QUERY_GET_CONTROL_KWARGS,
        settings.QUERY_GET_ES_KWARGS, settings.QUERY_GET_ESQB_KWARGS,
        settings.QUERY_GET_TRANSFORM_KWARGS, settings.
        QUERY_POST_CONTROL_KWARGS, settings.QUERY_POST_ES_KWARGS, settings.
        QUERY_POST_ESQB_KWARGS, settings.QUERY_POST_TRANSFORM_KWARGS,
        settings.METADATA_GET_CONTROL_KWARGS, settings.
        METADATA_GET_ES_KWARGS, settings.METADATA_GET_ESQB_KWARGS, settings
        .METADATA_GET_TRANSFORM_KWARGS]:
        for k, v in d.items():
            all_params.setdefault(k, v)
    return all_params


def _query_get_description(settings, biothing_object, url):
    _descriptions = getattr(settings, 'KWARG_DESCRIPTIONS', {})
    _parameters = [OrderedDict([('name', 'q'), ('in', 'query'), (
        'description', _descriptions.get('q', {}).get('text_template').
        format(doc_query_syntax_url='')), ('required', True), ('example',
        getattr(settings, 'STATUS_CHECK', {}).get('id', 'EXAMPLE')), (
        'schema', {'type': 'string'})])]
    for k in _query_get_params(settings).keys():
        _parameters.append(OrderedDict([('name', _descriptions.get(k).get(
            'name')), ('$ref', '#/components/parameters/{}'.format(
            _descriptions.get(k).get('name')))]))
    return OrderedDict([('tags', ['query']), ('summary',
        'Make {biothing_object} queries and return matching {biothing_object} hits. Supports JSONP and CORS as well.'
        .format(biothing_object=biothing_object)), ('parameters',
        _parameters), ('responses', OrderedDict([('200', OrderedDict([(
        'description', 'A query response with the "hits" property'), (
        'content', {'application/json': {'schema': {'$ref':
        '#/components/schemas/QueryResult'}}})])), ('400', OrderedDict([(
        'description',
        'A response indicating an improperly formatted query'), ('content',
        {'application/json': {'schema': {'$ref':
        '#/components/schemas/ErrorResult'}}})]))]))])


def _query_post_description(settings, biothing_object, url):
    _descriptions = getattr(settings, 'KWARG_DESCRIPTIONS', {})
    _parameters = []
    for k in _query_post_params(settings).keys():
        _parameters.append(OrderedDict([('name', _descriptions.get(k).get(
            'name')), ('$ref', '#/components/parameters/{}'.format(
            _descriptions.get(k).get('name')))]))
    return OrderedDict([('tags', ['query']), ('summary',
        'Make {biothing_object} batch queries and return matching {biothing_object} hits'
        .format(biothing_object=biothing_object)), ('requestBody', {
        'content': {'application/x-www-form-urlencoded': {'schema': {
        'properties': OrderedDict([('q', OrderedDict([('description',
        'multiple query terms separated by comma (also "+" or whitespace).  Does not support wildcard queries'
        ), ('type', 'string')])), ('scopes', OrderedDict([('description',
        _descriptions.get('scopes', {}).get('text_template', '').format(
        param_type='string', param_max='')), ('type', 'string')]))])}}}}),
        ('parameters', _parameters), ('responses', OrderedDict([('200',
        OrderedDict([('description',
        'Query response objects with the "hits" property'), ('content', {
        'application/json': {'schema': {'$ref':
        '#/components/schemas/QueryPOSTResult'}}})])), ('400', OrderedDict(
        [('description',
        'A response indicating an improperly formatted query'), ('content',
        {'application/json': {'schema': {'$ref':
        '#/components/schemas/ErrorResult'}}})]))]))])


def _annotation_get_description(settings, biothing_object, url):
    _descriptions = getattr(settings, 'KWARG_DESCRIPTIONS', {})
    _parameters = [OrderedDict([('name', '{biothing_object}id'.format(
        biothing_object=biothing_object)), ('in', 'path'), ('description',
        '{biothing_object} id'.format(biothing_object=biothing_object)), (
        'required', True), ('example', getattr(settings, 'STATUS_CHECK', {}
        ).get('id', 'EXAMPLE')), ('schema', {'type': 'string'})])]
    for k in _annotation_get_params(settings).keys():
        _parameters.append(OrderedDict([('name', _descriptions.get(k).get(
            'name')), ('$ref', '#/components/parameters/{}'.format(
            _descriptions.get(k).get('name')))]))
    return OrderedDict([('tags', ['{}'.format(biothing_object)]), (
        'summary',
        'Retrieve {biothing_object} objects based on {biothing_object} id.  Supports JSONP and CORS as well.'
        .format(biothing_object=biothing_object)), ('parameters',
        _parameters), ('responses', OrderedDict([('200', OrderedDict([(
        'description', 'A matching {biothing_object} object'.format(
        biothing_object=biothing_object)), ('content', {'application/json':
        {'schema': {'$ref': '#/components/schemas/{}'.format(
        biothing_object.title())}}})])), ('404', OrderedDict([(
        'description',
        'A response indicating an unknown {biothing_object} id'.format(
        biothing_object=biothing_object))]))]))])


def _annotation_post_description(settings, biothing_object, url):
    _descriptions = getattr(settings, 'KWARG_DESCRIPTIONS', {})
    _parameters = []
    for k in _annotation_post_params(settings).keys():
        _parameters.append(OrderedDict([('name', _descriptions.get(k).get(
            'name')), ('$ref', '#/components/parameters/{}'.format(
            _descriptions.get(k).get('name')))]))
    return OrderedDict([('tags', ['{biothing_object}'.format(
        biothing_object=biothing_object)]), ('summary',
        'For a list of {biothing_object} ids, return the matching {biothing_object} object'
        .format(biothing_object=biothing_object)), ('requestBody', {
        'content': {'application/x-www-form-urlencoded': {'schema':
        OrderedDict([('properties', {'ids': OrderedDict([('description',
        _descriptions.get('ids', {}).get('text_template', '').format(
        biothing_object=biothing_object, param_type=' Type: string (list).',
        param_default_value='', param_max=_param_max(getattr(settings,
        'ANNOTATION_POST_CONTROL_KWARGS', {}).get('max', 1000)))), ('type',
        'string')])}), ('required', ['ids'])])}}}), ('parameters',
        _parameters), ('responses', OrderedDict([('200', OrderedDict([(
        'description', 'A list of matching {biothing_object} objects'.
        format(biothing_object=biothing_object)), ('content', {
        'application/json': {'schema': {'type': 'array', 'items': {'$ref':
        '#/components/schemas/{}'.format(biothing_object.title())}}}})])),
        ('400', OrderedDict([('description',
        'A response indicating an improperly formatted query'), ('content',
        {'application/json': {'schema': {'$ref':
        '#/components/schemas/ErrorResult'}}})]))]))])


def _metadata_description(settings, biothing_object, url):
    return OrderedDict([('tags', ['metadata']), ('summary',
        'Get metadata about the data available from {url}'.format(url=url)),
        ('parameters', [OrderedDict([('name', 'callback'), ('in', 'query'),
        ('$ref', '#/components/parameters/callback')])]), ('responses', {
        '200': {'description': '{url} metadata object'.format(url=url)}})])


def _metadata_fields_description(settings, biothing_object, url):
    _descriptions = getattr(settings, 'KWARG_DESCRIPTIONS', {})
    _parameters = []
    for k in _metadata_params(settings).keys():
        _parameters.append(OrderedDict([('name', _descriptions.get(k).get(
            'name')), ('$ref', '#/components/parameters/{}'.format(
            _descriptions.get(k).get('name')))]))
    return OrderedDict([('tags', ['metadata']), ('summary',
        'Get metadata about the data fields available from a {url} {biothing_object} object'
        .format(biothing_object=biothing_object, url=url)), ('parameters',
        _parameters), ('responses', {'200': {'description':
        '{url} metadata fields object'.format(url=url)}})])


def create_openapi_json(_settings, **kwargs):
    _url = getattr(_settings, 'GA_TRACKER_URL', 'MyBiothing.info')
    _biothing_object = getattr(_settings, 'ES_DOC_TYPE', 'biothing')
    _doc = OrderedDict([('openapi', kwargs.get('openapi', '3.0.0')), (
        'info', OrderedDict([('version', kwargs.get('info__version',
        '{:.1f}'.format(float(getattr(_settings, 'API_VERSION', 'v1').
        lstrip('v'))))), ('title', kwargs.get('info__title', '{} API'.
        format(getattr(_settings, 'GA_TRACKER_URL', 'MyBiothing.info')))),
        ('description', kwargs.get('info__description', getattr(_settings,
        'KWARG_DESCRIPTION', {}).get('_root',
        'Documentation of the {url} {biothing_object} query web services.  Learn more about [{url}](http://{url}/)'
        .format(url=_url, biothing_object=_biothing_object)))), (
        'termsOfService', kwargs.get('info__termsOfService',
        'http://{url}/terms'.format(url=_url))), ('contact', OrderedDict([(
        'name', kwargs.get('info__contact__name', 'Chunlei Wu')), ('x-role',
        kwargs.get('info__contact__x_role', 'responsible developer')), (
        'email', kwargs.get('info__contact__email', 'help@biothings.io')),
        ('x-id', kwargs.get('info__contact__x_id',
        'https://github.com/newgene'))]))])), ('servers', [OrderedDict([(
        'url', 'http://{url}/{version}'.format(url=_url, version=getattr(
        _settings, 'API_VERSION', 'v1'))), ('description',
        'Production server')]), OrderedDict([('url',
        'https://{url}/{version}'.format(url=_url, version=getattr(
        _settings, 'API_VERSION', 'v1'))), ('description',
        'Encrypted Production server')])]), ('tags', [{'name':
        '{biothing_object}'.format(biothing_object=_biothing_object)}, {
        'name': 'query'}, {'name': 'metadata'}]), ('paths', OrderedDict([(
        '/{biothing_object}/{{{biothing_object}id}}'.format(biothing_object
        =_biothing_object), OrderedDict([('get',
        _annotation_get_description(settings=_settings, biothing_object=
        _biothing_object, url=_url))])), ('/{biothing_object}'.format(
        biothing_object=_biothing_object), OrderedDict([('post',
        _annotation_post_description(settings=_settings, biothing_object=
        _biothing_object, url=_url))])), ('/query', OrderedDict([('get',
        _query_get_description(settings=_settings, biothing_object=
        _biothing_object, url=_url)), ('post', _query_post_description(
        settings=_settings, biothing_object=_biothing_object, url=_url))])),
        ('/metadata', OrderedDict([('get', _metadata_description(settings=
        _settings, biothing_object=_biothing_object, url=_url))])), (
        '/metadata/fields', OrderedDict([('get',
        _metadata_fields_description(settings=_settings, biothing_object=
        _biothing_object, url=_url))]))])), ('components', OrderedDict([(
        'parameters', _get_common_parameters(settings=_settings,
        biothing_object=_biothing_object, url=_url)), ('schemas',
        _get_schemas(settings=_settings, biothing_object=_biothing_object,
        url=_url))]))])
    return _doc


def create_openapi_yaml(settings, yaml_file, **kwargs):
    """Create an openapi compliant spec for the biothing project whose settings are input.

    settings - a biothings web settings object
    yaml_file - the output file
    """
    setup_yaml()
    _json = create_openapi_json(settings, **kwargs)
    with open(yaml_file, 'w') as outf:
        yaml.dump(_json, outf, default_flow_style=False)

<mask token>


def setup_yaml():
    """https://stackoverflow.com/a/8661021"""
    represent_dict_order = lambda self, data: self.represent_mapping(
        'tag:yaml.org,2002:map', data.items())
    yaml.add_representer(OrderedDict, represent_dict_order)


all_params = {}


def _get_common_parameters(settings, biothing_object, url):
    _all = _get_all_params(settings)
    _ret = OrderedDict([])
    _common_params_list = [(k, v) for k, v in _all.items() if k != 'q' and 
        k != 'scopes' and k != 'ids' and k in getattr(settings,
        'KWARG_DESCRIPTIONS', {})]
    _common_params_list.extend([('callback', {'type': str, 'default': None}
        ), ('email', {'type': str, 'default': None})])
    _description_templates = getattr(settings, 'KWARG_DESCRIPTIONS', {})
    for param, _description in _common_params_list:
        _description_template_object = _description_templates.get(param)
        _param_display = _description_template_object.get('name')
        _ret.update(OrderedDict([(_param_display, OrderedDict([('name',
            _param_display), ('in', 'query'), ('description',
            _description_template_object.get('text_template', '').format(
            biothing_object=biothing_object, param_type=_string_types(_all.
            get(param, {}).get('type', '')), param_default_value=
            _param_default_values(_all.get(param, {}).get('default', '')),
            param_max=_param_max(_all.get(param, {}).get('max', '')))), (
            'schema', {'type': _schema_types(_all.get(param, {}).get('type',
            ''))})]))]))
    return _ret


def _get_schemas(settings, biothing_object, url):
    return OrderedDict([('string_or_array', {'oneOf': [{'type': 'array',
        'items': {'type': 'string'}}, {'type': 'string'}]}), (
        'int64_or_array', {'oneOf': [{'type': 'array', 'items': {'type':
        'integer', 'format': 'int64'}}, {'type': 'integer', 'format':
        'int64'}]}), ('QueryResult', {'type': 'object', 'properties':
        OrderedDict([('max_score', {'type': 'number', 'format': 'float'}),
        ('took', {'type': 'integer'}), ('total', {'type': 'integer'}), (
        'hits', {'type': 'array', 'items': {'$ref':
        '#/components/schemas/{}'.format(biothing_object.title())}})])}), (
        'QueryPOSTResult', {'type': 'array', 'items': {'allOf': [{'$ref':
        '#/components/schemas/{}'.format(biothing_object.title())}, {'type':
        'object', 'properties': {'_score': {'type': 'number', 'format':
        'float'}, 'query': {'type': 'string'}}}]}}), ('ErrorResult', {
        'type': 'object', 'properties': {'success': {'type': 'boolean'},
        'message': {'type': 'string'}}}), ('{}'.format(biothing_object.
        title()), {'type': 'object', 'required': ['_id'], 'properties': {
        '_id': {'type': 'string'}}})])


def _schema_types(t):
    if issubclass(t, bool):
        return 'boolean'
    if issubclass(t, int):
        return 'integer'
    return 'string'


def _string_types(t):
    if issubclass(t, str):
        return ' Type: string.'
    elif issubclass(t, int):
        return ' Type: integer.'
    elif issubclass(t, list):
        return ' Type: string (list).'
    elif issubclass(t, bool):
        return ' Type: boolean.'
    return ' Type: string.'


def _param_default_values(d):
    if not d:
        return ' Default: None.'
    if isinstance(d, (int, str)):
        return ' Default: {}.'.format(d)
    elif isinstance(d, bool) and d:
        return ' Default: true.'
    elif isinstance(d, bool) and not d:
        return ' Default: false.'
    return ' Default: None.'


def _param_max(m):
    if not m:
        return ''
    return ' Max: {}.'.format(m)


def _query_get_params(settings):
    _ret = {}
    for d in [settings.QUERY_GET_CONTROL_KWARGS, settings.
        QUERY_GET_ES_KWARGS, settings.QUERY_GET_ESQB_KWARGS, settings.
        QUERY_GET_TRANSFORM_KWARGS]:
        for k, v in d.items():
            if k != 'q' and k in getattr(settings, 'KWARG_DESCRIPTIONS', {}):
                _ret.setdefault(k, v)
    _ret['callback'] = {'type': str, 'default': None}
    _ret['email'] = {'type': str, 'default': None}
    return _ret


def _metadata_params(settings):
    _ret = {}
    for d in [settings.METADATA_GET_CONTROL_KWARGS, settings.
        METADATA_GET_ES_KWARGS, settings.METADATA_GET_ESQB_KWARGS, settings
        .METADATA_GET_TRANSFORM_KWARGS]:
        for k, v in d.items():
            if k in getattr(settings, 'KWARG_DESCRIPTIONS', {}):
                _ret.setdefault(k, v)
    _ret['callback'] = {'type': str, 'default': None}
    return _ret


def _annotation_get_params(settings):
    _ret = {}
    for d in [settings.ANNOTATION_GET_CONTROL_KWARGS, settings.
        ANNOTATION_GET_ES_KWARGS, settings.ANNOTATION_GET_ESQB_KWARGS,
        settings.ANNOTATION_GET_TRANSFORM_KWARGS]:
        for k, v in d.items():
            if k != 'q' and k in getattr(settings, 'KWARG_DESCRIPTIONS', {}):
                _ret.setdefault(k, v)
    _ret['callback'] = {'type': str, 'default': None}
    _ret['email'] = {'type': str, 'default': None}
    return _ret


def _query_post_params(settings):
    _ret = {}
    for d in [settings.QUERY_POST_CONTROL_KWARGS, settings.
        QUERY_POST_ES_KWARGS, settings.QUERY_POST_ESQB_KWARGS, settings.
        QUERY_POST_TRANSFORM_KWARGS]:
        for k, v in d.items():
            if k != 'q' and k != 'scopes' and k in getattr(settings,
                'KWARG_DESCRIPTIONS', {}):
                _ret.setdefault(k, v)
    _ret['email'] = {'type': str, 'default': None}
    return _ret


def _annotation_post_params(settings):
    _ret = {}
    for d in [settings.ANNOTATION_POST_CONTROL_KWARGS, settings.
        ANNOTATION_POST_ES_KWARGS, settings.ANNOTATION_POST_ESQB_KWARGS,
        settings.ANNOTATION_POST_TRANSFORM_KWARGS]:
        for k, v in d.items():
            if k != 'ids' and k in getattr(settings, 'KWARG_DESCRIPTIONS', {}):
                _ret.setdefault(k, v)
    _ret['email'] = {'type': str, 'default': None}
    return _ret


def _get_all_params(settings):
    if all_params:
        return all_params
    for d in [settings.ANNOTATION_GET_CONTROL_KWARGS, settings.
        ANNOTATION_GET_ES_KWARGS, settings.ANNOTATION_GET_ESQB_KWARGS,
        settings.ANNOTATION_GET_TRANSFORM_KWARGS, settings.
        ANNOTATION_POST_CONTROL_KWARGS, settings.ANNOTATION_POST_ES_KWARGS,
        settings.ANNOTATION_POST_ESQB_KWARGS, settings.
        ANNOTATION_POST_TRANSFORM_KWARGS, settings.QUERY_GET_CONTROL_KWARGS,
        settings.QUERY_GET_ES_KWARGS, settings.QUERY_GET_ESQB_KWARGS,
        settings.QUERY_GET_TRANSFORM_KWARGS, settings.
        QUERY_POST_CONTROL_KWARGS, settings.QUERY_POST_ES_KWARGS, settings.
        QUERY_POST_ESQB_KWARGS, settings.QUERY_POST_TRANSFORM_KWARGS,
        settings.METADATA_GET_CONTROL_KWARGS, settings.
        METADATA_GET_ES_KWARGS, settings.METADATA_GET_ESQB_KWARGS, settings
        .METADATA_GET_TRANSFORM_KWARGS]:
        for k, v in d.items():
            all_params.setdefault(k, v)
    return all_params


def _query_get_description(settings, biothing_object, url):
    _descriptions = getattr(settings, 'KWARG_DESCRIPTIONS', {})
    _parameters = [OrderedDict([('name', 'q'), ('in', 'query'), (
        'description', _descriptions.get('q', {}).get('text_template').
        format(doc_query_syntax_url='')), ('required', True), ('example',
        getattr(settings, 'STATUS_CHECK', {}).get('id', 'EXAMPLE')), (
        'schema', {'type': 'string'})])]
    for k in _query_get_params(settings).keys():
        _parameters.append(OrderedDict([('name', _descriptions.get(k).get(
            'name')), ('$ref', '#/components/parameters/{}'.format(
            _descriptions.get(k).get('name')))]))
    return OrderedDict([('tags', ['query']), ('summary',
        'Make {biothing_object} queries and return matching {biothing_object} hits. Supports JSONP and CORS as well.'
        .format(biothing_object=biothing_object)), ('parameters',
        _parameters), ('responses', OrderedDict([('200', OrderedDict([(
        'description', 'A query response with the "hits" property'), (
        'content', {'application/json': {'schema': {'$ref':
        '#/components/schemas/QueryResult'}}})])), ('400', OrderedDict([(
        'description',
        'A response indicating an improperly formatted query'), ('content',
        {'application/json': {'schema': {'$ref':
        '#/components/schemas/ErrorResult'}}})]))]))])


def _query_post_description(settings, biothing_object, url):
    _descriptions = getattr(settings, 'KWARG_DESCRIPTIONS', {})
    _parameters = []
    for k in _query_post_params(settings).keys():
        _parameters.append(OrderedDict([('name', _descriptions.get(k).get(
            'name')), ('$ref', '#/components/parameters/{}'.format(
            _descriptions.get(k).get('name')))]))
    return OrderedDict([('tags', ['query']), ('summary',
        'Make {biothing_object} batch queries and return matching {biothing_object} hits'
        .format(biothing_object=biothing_object)), ('requestBody', {
        'content': {'application/x-www-form-urlencoded': {'schema': {
        'properties': OrderedDict([('q', OrderedDict([('description',
        'multiple query terms separated by comma (also "+" or whitespace).  Does not support wildcard queries'
        ), ('type', 'string')])), ('scopes', OrderedDict([('description',
        _descriptions.get('scopes', {}).get('text_template', '').format(
        param_type='string', param_max='')), ('type', 'string')]))])}}}}),
        ('parameters', _parameters), ('responses', OrderedDict([('200',
        OrderedDict([('description',
        'Query response objects with the "hits" property'), ('content', {
        'application/json': {'schema': {'$ref':
        '#/components/schemas/QueryPOSTResult'}}})])), ('400', OrderedDict(
        [('description',
        'A response indicating an improperly formatted query'), ('content',
        {'application/json': {'schema': {'$ref':
        '#/components/schemas/ErrorResult'}}})]))]))])


def _annotation_get_description(settings, biothing_object, url):
    _descriptions = getattr(settings, 'KWARG_DESCRIPTIONS', {})
    _parameters = [OrderedDict([('name', '{biothing_object}id'.format(
        biothing_object=biothing_object)), ('in', 'path'), ('description',
        '{biothing_object} id'.format(biothing_object=biothing_object)), (
        'required', True), ('example', getattr(settings, 'STATUS_CHECK', {}
        ).get('id', 'EXAMPLE')), ('schema', {'type': 'string'})])]
    for k in _annotation_get_params(settings).keys():
        _parameters.append(OrderedDict([('name', _descriptions.get(k).get(
            'name')), ('$ref', '#/components/parameters/{}'.format(
            _descriptions.get(k).get('name')))]))
    return OrderedDict([('tags', ['{}'.format(biothing_object)]), (
        'summary',
        'Retrieve {biothing_object} objects based on {biothing_object} id.  Supports JSONP and CORS as well.'
        .format(biothing_object=biothing_object)), ('parameters',
        _parameters), ('responses', OrderedDict([('200', OrderedDict([(
        'description', 'A matching {biothing_object} object'.format(
        biothing_object=biothing_object)), ('content', {'application/json':
        {'schema': {'$ref': '#/components/schemas/{}'.format(
        biothing_object.title())}}})])), ('404', OrderedDict([(
        'description',
        'A response indicating an unknown {biothing_object} id'.format(
        biothing_object=biothing_object))]))]))])


def _annotation_post_description(settings, biothing_object, url):
    _descriptions = getattr(settings, 'KWARG_DESCRIPTIONS', {})
    _parameters = []
    for k in _annotation_post_params(settings).keys():
        _parameters.append(OrderedDict([('name', _descriptions.get(k).get(
            'name')), ('$ref', '#/components/parameters/{}'.format(
            _descriptions.get(k).get('name')))]))
    return OrderedDict([('tags', ['{biothing_object}'.format(
        biothing_object=biothing_object)]), ('summary',
        'For a list of {biothing_object} ids, return the matching {biothing_object} object'
        .format(biothing_object=biothing_object)), ('requestBody', {
        'content': {'application/x-www-form-urlencoded': {'schema':
        OrderedDict([('properties', {'ids': OrderedDict([('description',
        _descriptions.get('ids', {}).get('text_template', '').format(
        biothing_object=biothing_object, param_type=' Type: string (list).',
        param_default_value='', param_max=_param_max(getattr(settings,
        'ANNOTATION_POST_CONTROL_KWARGS', {}).get('max', 1000)))), ('type',
        'string')])}), ('required', ['ids'])])}}}), ('parameters',
        _parameters), ('responses', OrderedDict([('200', OrderedDict([(
        'description', 'A list of matching {biothing_object} objects'.
        format(biothing_object=biothing_object)), ('content', {
        'application/json': {'schema': {'type': 'array', 'items': {'$ref':
        '#/components/schemas/{}'.format(biothing_object.title())}}}})])),
        ('400', OrderedDict([('description',
        'A response indicating an improperly formatted query'), ('content',
        {'application/json': {'schema': {'$ref':
        '#/components/schemas/ErrorResult'}}})]))]))])


def _metadata_description(settings, biothing_object, url):
    return OrderedDict([('tags', ['metadata']), ('summary',
        'Get metadata about the data available from {url}'.format(url=url)),
        ('parameters', [OrderedDict([('name', 'callback'), ('in', 'query'),
        ('$ref', '#/components/parameters/callback')])]), ('responses', {
        '200': {'description': '{url} metadata object'.format(url=url)}})])


def _metadata_fields_description(settings, biothing_object, url):
    _descriptions = getattr(settings, 'KWARG_DESCRIPTIONS', {})
    _parameters = []
    for k in _metadata_params(settings).keys():
        _parameters.append(OrderedDict([('name', _descriptions.get(k).get(
            'name')), ('$ref', '#/components/parameters/{}'.format(
            _descriptions.get(k).get('name')))]))
    return OrderedDict([('tags', ['metadata']), ('summary',
        'Get metadata about the data fields available from a {url} {biothing_object} object'
        .format(biothing_object=biothing_object, url=url)), ('parameters',
        _parameters), ('responses', {'200': {'description':
        '{url} metadata fields object'.format(url=url)}})])


def create_openapi_json(_settings, **kwargs):
    _url = getattr(_settings, 'GA_TRACKER_URL', 'MyBiothing.info')
    _biothing_object = getattr(_settings, 'ES_DOC_TYPE', 'biothing')
    _doc = OrderedDict([('openapi', kwargs.get('openapi', '3.0.0')), (
        'info', OrderedDict([('version', kwargs.get('info__version',
        '{:.1f}'.format(float(getattr(_settings, 'API_VERSION', 'v1').
        lstrip('v'))))), ('title', kwargs.get('info__title', '{} API'.
        format(getattr(_settings, 'GA_TRACKER_URL', 'MyBiothing.info')))),
        ('description', kwargs.get('info__description', getattr(_settings,
        'KWARG_DESCRIPTION', {}).get('_root',
        'Documentation of the {url} {biothing_object} query web services.  Learn more about [{url}](http://{url}/)'
        .format(url=_url, biothing_object=_biothing_object)))), (
        'termsOfService', kwargs.get('info__termsOfService',
        'http://{url}/terms'.format(url=_url))), ('contact', OrderedDict([(
        'name', kwargs.get('info__contact__name', 'Chunlei Wu')), ('x-role',
        kwargs.get('info__contact__x_role', 'responsible developer')), (
        'email', kwargs.get('info__contact__email', 'help@biothings.io')),
        ('x-id', kwargs.get('info__contact__x_id',
        'https://github.com/newgene'))]))])), ('servers', [OrderedDict([(
        'url', 'http://{url}/{version}'.format(url=_url, version=getattr(
        _settings, 'API_VERSION', 'v1'))), ('description',
        'Production server')]), OrderedDict([('url',
        'https://{url}/{version}'.format(url=_url, version=getattr(
        _settings, 'API_VERSION', 'v1'))), ('description',
        'Encrypted Production server')])]), ('tags', [{'name':
        '{biothing_object}'.format(biothing_object=_biothing_object)}, {
        'name': 'query'}, {'name': 'metadata'}]), ('paths', OrderedDict([(
        '/{biothing_object}/{{{biothing_object}id}}'.format(biothing_object
        =_biothing_object), OrderedDict([('get',
        _annotation_get_description(settings=_settings, biothing_object=
        _biothing_object, url=_url))])), ('/{biothing_object}'.format(
        biothing_object=_biothing_object), OrderedDict([('post',
        _annotation_post_description(settings=_settings, biothing_object=
        _biothing_object, url=_url))])), ('/query', OrderedDict([('get',
        _query_get_description(settings=_settings, biothing_object=
        _biothing_object, url=_url)), ('post', _query_post_description(
        settings=_settings, biothing_object=_biothing_object, url=_url))])),
        ('/metadata', OrderedDict([('get', _metadata_description(settings=
        _settings, biothing_object=_biothing_object, url=_url))])), (
        '/metadata/fields', OrderedDict([('get',
        _metadata_fields_description(settings=_settings, biothing_object=
        _biothing_object, url=_url))]))])), ('components', OrderedDict([(
        'parameters', _get_common_parameters(settings=_settings,
        biothing_object=_biothing_object, url=_url)), ('schemas',
        _get_schemas(settings=_settings, biothing_object=_biothing_object,
        url=_url))]))])
    return _doc


def create_openapi_yaml(settings, yaml_file, **kwargs):
    """Create an openapi compliant spec for the biothing project whose settings are input.

    settings - a biothings web settings object
    yaml_file - the output file
    """
    setup_yaml()
    _json = create_openapi_json(settings, **kwargs)
    with open(yaml_file, 'w') as outf:
        yaml.dump(_json, outf, default_flow_style=False)

# NOTE
# THIS MODULE IS FOR REFERENCE ONLY
# AND IS NO LONGER COMPATIBLE WITH THE LATEST CODEBASE

"""
    Utils for creating swagger spec from a config_web module.

"""
from collections import OrderedDict

import yaml


def setup_yaml():
    """https://stackoverflow.com/a/8661021"""
    represent_dict_order = lambda self, data: self.represent_mapping("tag:yaml.org,2002:map", data.items())
    yaml.add_representer(OrderedDict, represent_dict_order)


all_params = {}


def _get_common_parameters(settings, biothing_object, url):
    _all = _get_all_params(settings)
    _ret = OrderedDict([])
    # get all params except for q, scopes, ids -> these are added to the request body, and not the query string
    _common_params_list = [
        (k, v)
        for (k, v) in _all.items()
        if (k != "q") and (k != "scopes") and (k != "ids") and (k in getattr(settings, "KWARG_DESCRIPTIONS", {}))
    ]
    # add 'special' parameters not controlled by main biothings web pipeline
    _common_params_list.extend(
        [("callback", {"type": str, "default": None}), ("email", {"type": str, "default": None})]
    )
    _description_templates = getattr(settings, "KWARG_DESCRIPTIONS", {})
    for param, _description in _common_params_list:
        _description_template_object = _description_templates.get(param)
        _param_display = _description_template_object.get("name")
        _ret.update(
            OrderedDict(
                [
                    (
                        _param_display,
                        OrderedDict(
                            [
                                ("name", _param_display),
                                ("in", "query"),
                                (
                                    "description",
                                    _description_template_object.get("text_template", "").format(
                                        biothing_object=biothing_object,
                                        param_type=_string_types(_all.get(param, {}).get("type", "")),
                                        param_default_value=_param_default_values(
                                            _all.get(param, {}).get("default", "")
                                        ),
                                        param_max=_param_max(_all.get(param, {}).get("max", "")),
                                    ),
                                ),
                                ("schema", {"type": _schema_types(_all.get(param, {}).get("type", ""))}),
                            ]
                        ),
                    )
                ]
            )
        )
    return _ret


def _get_schemas(settings, biothing_object, url):
    return OrderedDict(
        [
            (
                "string_or_array",
                {
                    "oneOf": [
                        {"type": "array", "items": {"type": "string"}},
                        {"type": "string"},
                    ]
                },
            ),
            (
                "int64_or_array",
                {
                    "oneOf": [
                        {"type": "array", "items": {"type": "integer", "format": "int64"}},
                        {"type": "integer", "format": "int64"},
                    ]
                },
            ),
            (
                "QueryResult",
                {
                    "type": "object",
                    "properties": OrderedDict(
                        [
                            ("max_score", {"type": "number", "format": "float"}),
                            ("took", {"type": "integer"}),
                            ("total", {"type": "integer"}),
                            (
                                "hits",
                                {
                                    "type": "array",
                                    "items": {"$ref": "#/components/schemas/{}".format(biothing_object.title())},
                                },
                            ),
                        ]
                    ),
                },
            ),
            (
                "QueryPOSTResult",
                {
                    "type": "array",
                    "items": {
                        "allOf": [
                            {"$ref": "#/components/schemas/{}".format(biothing_object.title())},
                            {
                                "type": "object",
                                "properties": {
                                    "_score": {"type": "number", "format": "float"},
                                    "query": {"type": "string"},
                                },
                            },
                        ]
                    },
                },
            ),
            (
                "ErrorResult",
                {"type": "object", "properties": {"success": {"type": "boolean"}, "message": {"type": "string"}}},
            ),
            (
                "{}".format(biothing_object.title()),
                {"type": "object", "required": ["_id"], "properties": {"_id": {"type": "string"}}},
            ),
        ]
    )


def _schema_types(t):
    # if t == type(bool()):
    if issubclass(t, bool):
        return "boolean"
    # if t == type(int()):
    if issubclass(t, int):
        return "integer"
    return "string"


def _string_types(t):
    # if t == type(str()):
    if issubclass(t, str):
        return " Type: string."
    # elif t == type(int()):
    elif issubclass(t, int):
        return " Type: integer."
    # elif t == type([]):
    elif issubclass(t, list):
        return " Type: string (list)."
    # elif t == type(bool()):
    elif issubclass(t, bool):
        return " Type: boolean."
    return " Type: string."


def _param_default_values(d):
    if not d:
        return " Default: None."
    # if type(d) == type(int()) or type(d) == type(str()):
    if isinstance(d, (int, str)):
        return " Default: {}.".format(d)
    # elif type(d) == type(True) and d:
    elif isinstance(d, bool) and d:
        return " Default: true."
    # elif type(d) == type(False) and not d:
    elif isinstance(d, bool) and not d:
        return " Default: false."
    return " Default: None."


def _param_max(m):
    if not m:
        return ""
    return " Max: {}.".format(m)


def _query_get_params(settings):
    _ret = {}
    for d in [
        settings.QUERY_GET_CONTROL_KWARGS,
        settings.QUERY_GET_ES_KWARGS,
        settings.QUERY_GET_ESQB_KWARGS,
        settings.QUERY_GET_TRANSFORM_KWARGS,
    ]:
        for k, v in d.items():
            if k != "q" and k in getattr(settings, "KWARG_DESCRIPTIONS", {}):
                _ret.setdefault(k, v)

    _ret["callback"] = {"type": str, "default": None}
    _ret["email"] = {"type": str, "default": None}

    return _ret


def _metadata_params(settings):
    _ret = {}
    for d in [
        settings.METADATA_GET_CONTROL_KWARGS,
        settings.METADATA_GET_ES_KWARGS,
        settings.METADATA_GET_ESQB_KWARGS,
        settings.METADATA_GET_TRANSFORM_KWARGS,
    ]:
        for k, v in d.items():
            if k in getattr(settings, "KWARG_DESCRIPTIONS", {}):
                _ret.setdefault(k, v)

    _ret["callback"] = {"type": str, "default": None}

    return _ret


def _annotation_get_params(settings):
    _ret = {}
    for d in [
        settings.ANNOTATION_GET_CONTROL_KWARGS,
        settings.ANNOTATION_GET_ES_KWARGS,
        settings.ANNOTATION_GET_ESQB_KWARGS,
        settings.ANNOTATION_GET_TRANSFORM_KWARGS,
    ]:
        for k, v in d.items():
            if k != "q" and k in getattr(settings, "KWARG_DESCRIPTIONS", {}):
                _ret.setdefault(k, v)

    _ret["callback"] = {"type": str, "default": None}
    _ret["email"] = {"type": str, "default": None}

    return _ret


def _query_post_params(settings):
    _ret = {}
    for d in [
        settings.QUERY_POST_CONTROL_KWARGS,
        settings.QUERY_POST_ES_KWARGS,
        settings.QUERY_POST_ESQB_KWARGS,
        settings.QUERY_POST_TRANSFORM_KWARGS,
    ]:
        for k, v in d.items():
            if k != "q" and k != "scopes" and k in getattr(settings, "KWARG_DESCRIPTIONS", {}):
                _ret.setdefault(k, v)

    _ret["email"] = {"type": str, "default": None}

    return _ret


def _annotation_post_params(settings):
    _ret = {}
    for d in [
        settings.ANNOTATION_POST_CONTROL_KWARGS,
        settings.ANNOTATION_POST_ES_KWARGS,
        settings.ANNOTATION_POST_ESQB_KWARGS,
        settings.ANNOTATION_POST_TRANSFORM_KWARGS,
    ]:
        for k, v in d.items():
            if k != "ids" and k in getattr(settings, "KWARG_DESCRIPTIONS", {}):
                _ret.setdefault(k, v)

    _ret["email"] = {"type": str, "default": None}

    return _ret


def _get_all_params(settings):
    # returns all possible parameters for a biothing web settings object
    if all_params:
        return all_params

    for d in [
        settings.ANNOTATION_GET_CONTROL_KWARGS,
        settings.ANNOTATION_GET_ES_KWARGS,
        settings.ANNOTATION_GET_ESQB_KWARGS,
        settings.ANNOTATION_GET_TRANSFORM_KWARGS,
        settings.ANNOTATION_POST_CONTROL_KWARGS,
        settings.ANNOTATION_POST_ES_KWARGS,
        settings.ANNOTATION_POST_ESQB_KWARGS,
        settings.ANNOTATION_POST_TRANSFORM_KWARGS,
        settings.QUERY_GET_CONTROL_KWARGS,
        settings.QUERY_GET_ES_KWARGS,
        settings.QUERY_GET_ESQB_KWARGS,
        settings.QUERY_GET_TRANSFORM_KWARGS,
        settings.QUERY_POST_CONTROL_KWARGS,
        settings.QUERY_POST_ES_KWARGS,
        settings.QUERY_POST_ESQB_KWARGS,
        settings.QUERY_POST_TRANSFORM_KWARGS,
        settings.METADATA_GET_CONTROL_KWARGS,
        settings.METADATA_GET_ES_KWARGS,
        settings.METADATA_GET_ESQB_KWARGS,
        settings.METADATA_GET_TRANSFORM_KWARGS,
    ]:
        for k, v in d.items():
            all_params.setdefault(k, v)

    return all_params


def _query_get_description(settings, biothing_object, url):
    _descriptions = getattr(settings, "KWARG_DESCRIPTIONS", {})
    _parameters = [
        OrderedDict(
            [
                ("name", "q"),
                ("in", "query"),
                ("description", _descriptions.get("q", {}).get("text_template").format(doc_query_syntax_url="")),
                ("required", True),
                ("example", getattr(settings, "STATUS_CHECK", {}).get("id", "EXAMPLE")),
                ("schema", {"type": "string"}),
            ]
        )
    ]
    for k in _query_get_params(settings).keys():
        _parameters.append(
            OrderedDict(
                [
                    ("name", _descriptions.get(k).get("name")),
                    ("$ref", "#/components/parameters/{}".format(_descriptions.get(k).get("name"))),
                ]
            )
        )
    return OrderedDict(
        [
            ("tags", ["query"]),
            (
                "summary",
                "Make {biothing_object} queries and return matching {biothing_object} hits. Supports JSONP and CORS as well.".format(
                    biothing_object=biothing_object
                ),
            ),
            ("parameters", _parameters),
            (
                "responses",
                OrderedDict(
                    [
                        (
                            "200",
                            OrderedDict(
                                [
                                    ("description", 'A query response with the "hits" property'),
                                    (
                                        "content",
                                        {"application/json": {"schema": {"$ref": "#/components/schemas/QueryResult"}}},
                                    ),
                                ]
                            ),
                        ),
                        (
                            "400",
                            OrderedDict(
                                [
                                    ("description", "A response indicating an improperly formatted query"),
                                    (
                                        "content",
                                        {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResult"}}},
                                    ),
                                ]
                            ),
                        ),
                    ]
                ),
            ),
        ]
    )


def _query_post_description(settings, biothing_object, url):
    _descriptions = getattr(settings, "KWARG_DESCRIPTIONS", {})
    _parameters = []
    for k in _query_post_params(settings).keys():
        _parameters.append(
            OrderedDict(
                [
                    ("name", _descriptions.get(k).get("name")),
                    ("$ref", "#/components/parameters/{}".format(_descriptions.get(k).get("name"))),
                ]
            )
        )
    return OrderedDict(
        [
            ("tags", ["query"]),
            (
                "summary",
                "Make {biothing_object} batch queries and return matching {biothing_object} hits".format(
                    biothing_object=biothing_object
                ),
            ),
            (
                "requestBody",
                {
                    "content": {
                        "application/x-www-form-urlencoded": {
                            "schema": {
                                "properties": OrderedDict(
                                    [
                                        (
                                            "q",
                                            OrderedDict(
                                                [
                                                    (
                                                        "description",
                                                        'multiple query terms separated by comma (also "+" or whitespace).  Does not support wildcard queries',
                                                    ),
                                                    ("type", "string"),
                                                ]
                                            ),
                                        ),
                                        (
                                            "scopes",
                                            OrderedDict(
                                                [
                                                    (
                                                        "description",
                                                        _descriptions.get("scopes", {})
                                                        .get("text_template", "")
                                                        .format(param_type="string", param_max=""),
                                                    ),
                                                    ("type", "string"),
                                                ]
                                            ),
                                        ),
                                    ]
                                )
                            }
                        }
                    }
                },
            ),
            ("parameters", _parameters),
            (
                "responses",
                OrderedDict(
                    [
                        (
                            "200",
                            OrderedDict(
                                [
                                    ("description", 'Query response objects with the "hits" property'),
                                    (
                                        "content",
                                        {
                                            "application/json": {
                                                "schema": {"$ref": "#/components/schemas/QueryPOSTResult"}
                                            }
                                        },
                                    ),
                                ]
                            ),
                        ),
                        (
                            "400",
                            OrderedDict(
                                [
                                    ("description", "A response indicating an improperly formatted query"),
                                    (
                                        "content",
                                        {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResult"}}},
                                    ),
                                ]
                            ),
                        ),
                    ]
                ),
            ),
        ]
    )


def _annotation_get_description(settings, biothing_object, url):
    _descriptions = getattr(settings, "KWARG_DESCRIPTIONS", {})
    _parameters = [
        OrderedDict(
            [
                ("name", "{biothing_object}id".format(biothing_object=biothing_object)),
                ("in", "path"),
                ("description", "{biothing_object} id".format(biothing_object=biothing_object)),
                ("required", True),
                ("example", getattr(settings, "STATUS_CHECK", {}).get("id", "EXAMPLE")),
                ("schema", {"type": "string"}),
            ]
        )
    ]
    for k in _annotation_get_params(settings).keys():
        _parameters.append(
            OrderedDict(
                [
                    ("name", _descriptions.get(k).get("name")),
                    ("$ref", "#/components/parameters/{}".format(_descriptions.get(k).get("name"))),
                ]
            )
        )
    return OrderedDict(
        [
            ("tags", ["{}".format(biothing_object)]),
            (
                "summary",
                "Retrieve {biothing_object} objects based on {biothing_object} id.  Supports JSONP and CORS as well.".format(
                    biothing_object=biothing_object
                ),
            ),
            ("parameters", _parameters),
            (
                "responses",
                OrderedDict(
                    [
                        (
                            "200",
                            OrderedDict(
                                [
                                    (
                                        "description",
                                        "A matching {biothing_object} object".format(biothing_object=biothing_object),
                                    ),
                                    (
                                        "content",
                                        {
                                            "application/json": {
                                                "schema": {
                                                    "$ref": "#/components/schemas/{}".format(biothing_object.title())
                                                }
                                            }
                                        },
                                    ),
                                ]
                            ),
                        ),
                        (
                            "404",
                            OrderedDict(
                                [
                                    (
                                        "description",
                                        "A response indicating an unknown {biothing_object} id".format(
                                            biothing_object=biothing_object
                                        ),
                                    )
                                ]
                            ),
                        ),
                    ]
                ),
            ),
        ]
    )


def _annotation_post_description(settings, biothing_object, url):
    _descriptions = getattr(settings, "KWARG_DESCRIPTIONS", {})
    _parameters = []
    for k in _annotation_post_params(settings).keys():
        _parameters.append(
            OrderedDict(
                [
                    ("name", _descriptions.get(k).get("name")),
                    ("$ref", "#/components/parameters/{}".format(_descriptions.get(k).get("name"))),
                ]
            )
        )
    return OrderedDict(
        [
            ("tags", ["{biothing_object}".format(biothing_object=biothing_object)]),
            (
                "summary",
                "For a list of {biothing_object} ids, return the matching {biothing_object} object".format(
                    biothing_object=biothing_object
                ),
            ),
            (
                "requestBody",
                {
                    "content": {
                        "application/x-www-form-urlencoded": {
                            "schema": OrderedDict(
                                [
                                    (
                                        "properties",
                                        {
                                            "ids": OrderedDict(
                                                [
                                                    (
                                                        "description",
                                                        _descriptions.get("ids", {})
                                                        .get("text_template", "")
                                                        .format(
                                                            biothing_object=biothing_object,
                                                            param_type=" Type: string (list).",
                                                            param_default_value="",
                                                            param_max=_param_max(
                                                                getattr(
                                                                    settings, "ANNOTATION_POST_CONTROL_KWARGS", {}
                                                                ).get("max", 1000)
                                                            ),
                                                        ),
                                                    ),
                                                    ("type", "string"),
                                                ]
                                            )
                                        },
                                    ),
                                    ("required", ["ids"]),
                                ]
                            )
                        }
                    }
                },
            ),
            ("parameters", _parameters),
            (
                "responses",
                OrderedDict(
                    [
                        (
                            "200",
                            OrderedDict(
                                [
                                    (
                                        "description",
                                        "A list of matching {biothing_object} objects".format(
                                            biothing_object=biothing_object
                                        ),
                                    ),
                                    (
                                        "content",
                                        {
                                            "application/json": {
                                                "schema": {
                                                    "type": "array",
                                                    "items": {
                                                        "$ref": "#/components/schemas/{}".format(
                                                            biothing_object.title()
                                                        )
                                                    },
                                                }
                                            }
                                        },
                                    ),
                                ]
                            ),
                        ),
                        (
                            "400",
                            OrderedDict(
                                [
                                    ("description", "A response indicating an improperly formatted query"),
                                    (
                                        "content",
                                        {"application/json": {"schema": {"$ref": "#/components/schemas/ErrorResult"}}},
                                    ),
                                ]
                            ),
                        ),
                    ]
                ),
            ),
        ]
    )


def _metadata_description(settings, biothing_object, url):
    return OrderedDict(
        [
            ("tags", ["metadata"]),
            ("summary", "Get metadata about the data available from {url}".format(url=url)),
            (
                "parameters",
                [OrderedDict([("name", "callback"), ("in", "query"), ("$ref", "#/components/parameters/callback")])],
            ),
            ("responses", {"200": {"description": "{url} metadata object".format(url=url)}}),
        ]
    )


def _metadata_fields_description(settings, biothing_object, url):
    _descriptions = getattr(settings, "KWARG_DESCRIPTIONS", {})
    _parameters = []
    for k in _metadata_params(settings).keys():
        _parameters.append(
            OrderedDict(
                [
                    ("name", _descriptions.get(k).get("name")),
                    ("$ref", "#/components/parameters/{}".format(_descriptions.get(k).get("name"))),
                ]
            )
        )
    return OrderedDict(
        [
            ("tags", ["metadata"]),
            (
                "summary",
                "Get metadata about the data fields available from a {url} {biothing_object} object".format(
                    biothing_object=biothing_object, url=url
                ),
            ),
            ("parameters", _parameters),
            ("responses", {"200": {"description": "{url} metadata fields object".format(url=url)}}),
        ]
    )


def create_openapi_json(_settings, **kwargs):
    _url = getattr(_settings, "GA_TRACKER_URL", "MyBiothing.info")
    _biothing_object = getattr(_settings, "ES_DOC_TYPE", "biothing")
    _doc = OrderedDict(
        [
            ("openapi", kwargs.get("openapi", "3.0.0")),
            (
                "info",
                OrderedDict(
                    [
                        (
                            "version",
                            kwargs.get(
                                "info__version",
                                "{:.1f}".format(float(getattr(_settings, "API_VERSION", "v1").lstrip("v"))),
                            ),
                        ),
                        (
                            "title",
                            kwargs.get(
                                "info__title", "{} API".format(getattr(_settings, "GA_TRACKER_URL", "MyBiothing.info"))
                            ),
                        ),
                        (
                            "description",
                            kwargs.get(
                                "info__description",
                                getattr(_settings, "KWARG_DESCRIPTION", {}).get(
                                    "_root",
                                    "Documentation of the {url} {biothing_object} query web services.  Learn more about [{url}](http://{url}/)".format(
                                        url=_url, biothing_object=_biothing_object
                                    ),
                                ),
                            ),
                        ),
                        ("termsOfService", kwargs.get("info__termsOfService", "http://{url}/terms".format(url=_url))),
                        (
                            "contact",
                            OrderedDict(
                                [
                                    ("name", kwargs.get("info__contact__name", "Chunlei Wu")),
                                    ("x-role", kwargs.get("info__contact__x_role", "responsible developer")),
                                    ("email", kwargs.get("info__contact__email", "help@biothings.io")),
                                    ("x-id", kwargs.get("info__contact__x_id", "https://github.com/newgene")),
                                ]
                            ),
                        ),
                    ]
                ),
            ),
            (
                "servers",
                [
                    OrderedDict(
                        [
                            (
                                "url",
                                "http://{url}/{version}".format(
                                    url=_url, version=getattr(_settings, "API_VERSION", "v1")
                                ),
                            ),
                            ("description", "Production server"),
                        ]
                    ),
                    OrderedDict(
                        [
                            (
                                "url",
                                "https://{url}/{version}".format(
                                    url=_url, version=getattr(_settings, "API_VERSION", "v1")
                                ),
                            ),
                            ("description", "Encrypted Production server"),
                        ]
                    ),
                ],
            ),
            (
                "tags",
                [
                    {"name": "{biothing_object}".format(biothing_object=_biothing_object)},
                    {"name": "query"},
                    {"name": "metadata"},
                ],
            ),
            (
                "paths",
                OrderedDict(
                    [
                        (
                            "/{biothing_object}/{{{biothing_object}id}}".format(biothing_object=_biothing_object),
                            # annotation GET
                            OrderedDict(
                                [
                                    (
                                        "get",
                                        _annotation_get_description(
                                            settings=_settings, biothing_object=_biothing_object, url=_url
                                        ),
                                    )
                                ]
                            ),
                        ),
                        # annotation POST
                        (
                            "/{biothing_object}".format(biothing_object=_biothing_object),
                            OrderedDict(
                                [
                                    (
                                        "post",
                                        _annotation_post_description(
                                            settings=_settings, biothing_object=_biothing_object, url=_url
                                        ),
                                    )
                                ]
                            ),
                        ),
                        # query GET/POST
                        (
                            "/query",
                            OrderedDict(
                                [
                                    (
                                        "get",
                                        _query_get_description(
                                            settings=_settings, biothing_object=_biothing_object, url=_url
                                        ),
                                    ),
                                    (
                                        "post",
                                        _query_post_description(
                                            settings=_settings, biothing_object=_biothing_object, url=_url
                                        ),
                                    ),
                                ]
                            ),
                        ),
                        # metadata GET
                        (
                            "/metadata",
                            OrderedDict(
                                [
                                    (
                                        "get",
                                        _metadata_description(
                                            settings=_settings, biothing_object=_biothing_object, url=_url
                                        ),
                                    )
                                ]
                            ),
                        ),
                        # metadata/fields GET
                        (
                            "/metadata/fields",
                            OrderedDict(
                                [
                                    (
                                        "get",
                                        _metadata_fields_description(
                                            settings=_settings, biothing_object=_biothing_object, url=_url
                                        ),
                                    )
                                ]
                            ),
                        ),
                    ]
                ),
            ),
            (
                "components",
                OrderedDict(
                    [
                        (
                            "parameters",
                            _get_common_parameters(settings=_settings, biothing_object=_biothing_object, url=_url),
                        ),
                        ("schemas", _get_schemas(settings=_settings, biothing_object=_biothing_object, url=_url)),
                    ]
                ),
            ),
        ]
    )
    return _doc


def create_openapi_yaml(settings, yaml_file, **kwargs):
    """Create an openapi compliant spec for the biothing project whose settings are input.

    settings - a biothings web settings object
    yaml_file - the output file
    """
    setup_yaml()
    _json = create_openapi_json(settings, **kwargs)
    with open(yaml_file, "w") as outf:
        yaml.dump(_json, outf, default_flow_style=False)
