prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  [优化] HTTP API 数据源同步报错
  对应API 没有按照要求, 返回`count/results`, 此时取空
  
  实际上这里的逻辑会`results`的第一条, 处理; 但是没有很好地校验
  
  会报错
  
  ```
  Task bkuser_core.categories.tasks.adapter_sync[78744136-5e83-4605-8503-d4d8ed90f9e3] raised unexpected: IndexError('list index out of range',)
  Traceback (most recent call last):
    File "/usr/local/lib/python3.6/site-packages/celery/app/trace.py", line 412, in trace_task
      R = retval = fun(*args, **kwargs)
    File "/usr/local/lib/python3.6/site-packages/celery/app/trace.py", line 704, in __protected_call__
      return self.run(*args, **kwargs)
    File "/usr/local/lib/python3.6/site-packages/celery/app/autoretry.py", line 50, in run
      raise task.retry(exc=exc, **retry_kwargs)
    File "/usr/local/lib/python3.6/site-packages/celery/app/task.py", line 706, in retry
      raise_with_context(exc)
    File "/usr/local/lib/python3.6/site-packages/celery/app/autoretry.py", line 35, in run
      return task._orig_run(*args, **kwargs)
    File "/app/bkuser_core/categories/tasks.py", line 111, in adapter_sync
      plugin.sync(instance_id=instance_id, task_id=task_id, *args, **kwargs)
    File "/app/bkuser_core/categories/plugins/plugin.py", line 124, in sync
      syncer.sync(*args, **kwargs)
    File "/app/bkuser_core/categories/plugins/custom/sycner.py", line 45, in sync
      self._sync_department()
    File "/app/bkuser_core/categories/plugins/custom/sycner.py", line 50, in _sync_department
      self._load2sync_manager(self.client.fetch_departments())
    File "/app/bkuser_core/categories/plugins/custom/sycner.py", line 74, in _load2sync_manager
      category=self.category, db_sync_manager=self.db_sync_manager, items=items, context=self.context
    File "/app/bkuser_core/categories/plugins/custom/helpers.py", line 277, in init_helper
      return _map[items.custom_type](
    File "/app/bkuser_core/categories/plugins/custom/models.py", line 81, in custom_type
      return type(list(self.items_map.values())[0])
  IndexError: list index out of range
  ```
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `src/api/bkuser_core/categories/plugins/custom/client.py`
  Content:
  ```
  1 # -*- coding: utf-8 -*-
  2 """
  3 TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-用户管理(Bk-User) available.
  4 Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved.
  5 Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
  6 You may obtain a copy of the License at http://opensource.org/licenses/MIT
  7 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  8 an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
  9 specific language governing permissions and limitations under the License.
  10 """
  11 import logging
  12 from dataclasses import dataclass
  13 from typing import Optional
  14 
  15 import curlify
  16 import requests
  17 
  18 from bkuser_core.categories.plugins.custom.exceptions import CustomAPIRequestFailed
  19 from bkuser_core.categories.plugins.custom.models import CustomDepartment, CustomProfile, CustomTypeList
  20 from bkuser_core.user_settings.loader import ConfigProvider
  21 
  22 logger = logging.getLogger(__name__)
  23 
  24 
  25 @dataclass
  26 class PageInfo:
  27     page: int
  28     page_size: int
  29 
  30 
  31 @dataclass
  32 class CustomDataClient:
  33     category_id: int
  34     api_host: str
  35     paths: dict
  36 
  37     @classmethod
  38     def from_config(cls):
  39         """从配置中创建客户端"""
  40 
  41     def __post_init__(self):
  42         self.config_loader = ConfigProvider(self.category_id)
  43 
  44     def _fetch_items(self, path: str):
  45         url = "/".join(s.strip("/") for s in [self.api_host, path])
  46         resp = requests.get(url, timeout=30)
  47 
  48         curl_format = curlify.to_curl(resp.request)
  49         logger.debug("going to call: %s", url)
  50         if resp.status_code >= 400:
  51             logger.error(
  52                 "failed to request api, status code: %s cUrl format: %s",
  53                 resp.status_code,
  54                 curl_format,
  55             )
  56             raise CustomAPIRequestFailed()
  57 
  58         try:
  59             resp_body = resp.json()
  60         except Exception as e:
  61             logger.exception("failed to parse resp as json, cUrl format: %s", curl_format)
  62             raise CustomAPIRequestFailed() from e
  63 
  64         # results not present in response body
  65         if "results" not in resp_body:
  66             logger.error("no `results` in response, cUrl format: %s", curl_format)
  67             raise CustomAPIRequestFailed("there got no `results` in response body")
  68 
  69         results = resp_body.get("results", [])
  70         # results not a list
  71         if not isinstance(results, list):
  72             logger.error("`results` in response is not a list, cUrl format: %s", curl_format)
  73             raise CustomAPIRequestFailed("the `results` in response is not a list")
  74 
  75         return results
  76 
  77     def fetch_profiles(self, page_info: Optional[PageInfo] = None) -> CustomTypeList:
  78         """获取 profile 对象列表"""
  79         results = self._fetch_items(path=self.paths["profile"])
  80         return CustomTypeList.from_list([CustomProfile.from_dict(x) for x in results])
  81 
  82     def fetch_departments(self, page_info: Optional[PageInfo] = None) -> CustomTypeList:
  83         """获取 department 对象列表"""
  84         results = self._fetch_items(path=self.paths["department"])
  85         return CustomTypeList.from_list([CustomDepartment.from_dict(x) for x in results])
  86
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/src/api/bkuser_core/categories/plugins/custom/client.py b/src/api/bkuser_core/categories/plugins/custom/client.py
  --- a/src/api/bkuser_core/categories/plugins/custom/client.py
  +++ b/src/api/bkuser_core/categories/plugins/custom/client.py
  @@ -72,6 +72,12 @@
               logger.error("`results` in response is not a list, cUrl format: %s", curl_format)
               raise CustomAPIRequestFailed("the `results` in response is not a list")
   
  +        # currently, if the results is empty, CustomTypeList.custom_type will raise IndexError(task fail)
  +        # so, here, we should check here: results size should not be empty
  +        if not results:
  +            logger.error("`results` in response is empty, cUrl format: %s", curl_format)
  +            raise CustomAPIRequestFailed("the `results` in response is empty")
  +
           return results
   
       def fetch_profiles(self, page_info: Optional[PageInfo] = None) -> CustomTypeList:
  
verification_info: {"golden_diff": "diff --git a/src/api/bkuser_core/categories/plugins/custom/client.py b/src/api/bkuser_core/categories/plugins/custom/client.py\n--- a/src/api/bkuser_core/categories/plugins/custom/client.py\n+++ b/src/api/bkuser_core/categories/plugins/custom/client.py\n@@ -72,6 +72,12 @@\n             logger.error(\"`results` in response is not a list, cUrl format: %s\", curl_format)\n             raise CustomAPIRequestFailed(\"the `results` in response is not a list\")\n \n+        # currently, if the results is empty, CustomTypeList.custom_type will raise IndexError(task fail)\n+        # so, here, we should check here: results size should not be empty\n+        if not results:\n+            logger.error(\"`results` in response is empty, cUrl format: %s\", curl_format)\n+            raise CustomAPIRequestFailed(\"the `results` in response is empty\")\n+\n         return results\n \n     def fetch_profiles(self, page_info: Optional[PageInfo] = None) -> CustomTypeList:\n", "issue": "[\u4f18\u5316] HTTP API \u6570\u636e\u6e90\u540c\u6b65\u62a5\u9519\n\u5bf9\u5e94API \u6ca1\u6709\u6309\u7167\u8981\u6c42, \u8fd4\u56de`count/results`, \u6b64\u65f6\u53d6\u7a7a\r\n\r\n\u5b9e\u9645\u4e0a\u8fd9\u91cc\u7684\u903b\u8f91\u4f1a`results`\u7684\u7b2c\u4e00\u6761, \u5904\u7406; \u4f46\u662f\u6ca1\u6709\u5f88\u597d\u5730\u6821\u9a8c\r\n\r\n\u4f1a\u62a5\u9519\r\n\r\n```\r\nTask bkuser_core.categories.tasks.adapter_sync[78744136-5e83-4605-8503-d4d8ed90f9e3] raised unexpected: IndexError('list index out of range',)\r\nTraceback (most recent call last):\r\n  File \"/usr/local/lib/python3.6/site-packages/celery/app/trace.py\", line 412, in trace_task\r\n    R = retval = fun(*args, **kwargs)\r\n  File \"/usr/local/lib/python3.6/site-packages/celery/app/trace.py\", line 704, in __protected_call__\r\n    return self.run(*args, **kwargs)\r\n  File \"/usr/local/lib/python3.6/site-packages/celery/app/autoretry.py\", line 50, in run\r\n    raise task.retry(exc=exc, **retry_kwargs)\r\n  File \"/usr/local/lib/python3.6/site-packages/celery/app/task.py\", line 706, in retry\r\n    raise_with_context(exc)\r\n  File \"/usr/local/lib/python3.6/site-packages/celery/app/autoretry.py\", line 35, in run\r\n    return task._orig_run(*args, **kwargs)\r\n  File \"/app/bkuser_core/categories/tasks.py\", line 111, in adapter_sync\r\n    plugin.sync(instance_id=instance_id, task_id=task_id, *args, **kwargs)\r\n  File \"/app/bkuser_core/categories/plugins/plugin.py\", line 124, in sync\r\n    syncer.sync(*args, **kwargs)\r\n  File \"/app/bkuser_core/categories/plugins/custom/sycner.py\", line 45, in sync\r\n    self._sync_department()\r\n  File \"/app/bkuser_core/categories/plugins/custom/sycner.py\", line 50, in _sync_department\r\n    self._load2sync_manager(self.client.fetch_departments())\r\n  File \"/app/bkuser_core/categories/plugins/custom/sycner.py\", line 74, in _load2sync_manager\r\n    category=self.category, db_sync_manager=self.db_sync_manager, items=items, context=self.context\r\n  File \"/app/bkuser_core/categories/plugins/custom/helpers.py\", line 277, in init_helper\r\n    return _map[items.custom_type](\r\n  File \"/app/bkuser_core/categories/plugins/custom/models.py\", line 81, in custom_type\r\n    return type(list(self.items_map.values())[0])\r\nIndexError: list index out of range\r\n```\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\"\"\"\nTencentBlueKing is pleased to support the open source community by making \u84dd\u9cb8\u667a\u4e91-\u7528\u6237\u7ba1\u7406(Bk-User) available.\nCopyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved.\nLicensed under the MIT License (the \"License\"); you may not use this file except in compliance with the License.\nYou may obtain a copy of the License at http://opensource.org/licenses/MIT\nUnless required by applicable law or agreed to in writing, software distributed under the License is distributed on\nan \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the\nspecific language governing permissions and limitations under the License.\n\"\"\"\nimport logging\nfrom dataclasses import dataclass\nfrom typing import Optional\n\nimport curlify\nimport requests\n\nfrom bkuser_core.categories.plugins.custom.exceptions import CustomAPIRequestFailed\nfrom bkuser_core.categories.plugins.custom.models import CustomDepartment, CustomProfile, CustomTypeList\nfrom bkuser_core.user_settings.loader import ConfigProvider\n\nlogger = logging.getLogger(__name__)\n\n\n@dataclass\nclass PageInfo:\n    page: int\n    page_size: int\n\n\n@dataclass\nclass CustomDataClient:\n    category_id: int\n    api_host: str\n    paths: dict\n\n    @classmethod\n    def from_config(cls):\n        \"\"\"\u4ece\u914d\u7f6e\u4e2d\u521b\u5efa\u5ba2\u6237\u7aef\"\"\"\n\n    def __post_init__(self):\n        self.config_loader = ConfigProvider(self.category_id)\n\n    def _fetch_items(self, path: str):\n        url = \"/\".join(s.strip(\"/\") for s in [self.api_host, path])\n        resp = requests.get(url, timeout=30)\n\n        curl_format = curlify.to_curl(resp.request)\n        logger.debug(\"going to call: %s\", url)\n        if resp.status_code >= 400:\n            logger.error(\n                \"failed to request api, status code: %s cUrl format: %s\",\n                resp.status_code,\n                curl_format,\n            )\n            raise CustomAPIRequestFailed()\n\n        try:\n            resp_body = resp.json()\n        except Exception as e:\n            logger.exception(\"failed to parse resp as json, cUrl format: %s\", curl_format)\n            raise CustomAPIRequestFailed() from e\n\n        # results not present in response body\n        if \"results\" not in resp_body:\n            logger.error(\"no `results` in response, cUrl format: %s\", curl_format)\n            raise CustomAPIRequestFailed(\"there got no `results` in response body\")\n\n        results = resp_body.get(\"results\", [])\n        # results not a list\n        if not isinstance(results, list):\n            logger.error(\"`results` in response is not a list, cUrl format: %s\", curl_format)\n            raise CustomAPIRequestFailed(\"the `results` in response is not a list\")\n\n        return results\n\n    def fetch_profiles(self, page_info: Optional[PageInfo] = None) -> CustomTypeList:\n        \"\"\"\u83b7\u53d6 profile \u5bf9\u8c61\u5217\u8868\"\"\"\n        results = self._fetch_items(path=self.paths[\"profile\"])\n        return CustomTypeList.from_list([CustomProfile.from_dict(x) for x in results])\n\n    def fetch_departments(self, page_info: Optional[PageInfo] = None) -> CustomTypeList:\n        \"\"\"\u83b7\u53d6 department \u5bf9\u8c61\u5217\u8868\"\"\"\n        results = self._fetch_items(path=self.paths[\"department\"])\n        return CustomTypeList.from_list([CustomDepartment.from_dict(x) for x in results])\n", "path": "src/api/bkuser_core/categories/plugins/custom/client.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n\"\"\"\nTencentBlueKing is pleased to support the open source community by making \u84dd\u9cb8\u667a\u4e91-\u7528\u6237\u7ba1\u7406(Bk-User) available.\nCopyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved.\nLicensed under the MIT License (the \"License\"); you may not use this file except in compliance with the License.\nYou may obtain a copy of the License at http://opensource.org/licenses/MIT\nUnless required by applicable law or agreed to in writing, software distributed under the License is distributed on\nan \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the\nspecific language governing permissions and limitations under the License.\n\"\"\"\nimport logging\nfrom dataclasses import dataclass\nfrom typing import Optional\n\nimport curlify\nimport requests\n\nfrom bkuser_core.categories.plugins.custom.exceptions import CustomAPIRequestFailed\nfrom bkuser_core.categories.plugins.custom.models import CustomDepartment, CustomProfile, CustomTypeList\nfrom bkuser_core.user_settings.loader import ConfigProvider\n\nlogger = logging.getLogger(__name__)\n\n\n@dataclass\nclass PageInfo:\n    page: int\n    page_size: int\n\n\n@dataclass\nclass CustomDataClient:\n    category_id: int\n    api_host: str\n    paths: dict\n\n    @classmethod\n    def from_config(cls):\n        \"\"\"\u4ece\u914d\u7f6e\u4e2d\u521b\u5efa\u5ba2\u6237\u7aef\"\"\"\n\n    def __post_init__(self):\n        self.config_loader = ConfigProvider(self.category_id)\n\n    def _fetch_items(self, path: str):\n        url = \"/\".join(s.strip(\"/\") for s in [self.api_host, path])\n        resp = requests.get(url, timeout=30)\n\n        curl_format = curlify.to_curl(resp.request)\n        logger.debug(\"going to call: %s\", url)\n        if resp.status_code >= 400:\n            logger.error(\n                \"failed to request api, status code: %s cUrl format: %s\",\n                resp.status_code,\n                curl_format,\n            )\n            raise CustomAPIRequestFailed()\n\n        try:\n            resp_body = resp.json()\n        except Exception as e:\n            logger.exception(\"failed to parse resp as json, cUrl format: %s\", curl_format)\n            raise CustomAPIRequestFailed() from e\n\n        # results not present in response body\n        if \"results\" not in resp_body:\n            logger.error(\"no `results` in response, cUrl format: %s\", curl_format)\n            raise CustomAPIRequestFailed(\"there got no `results` in response body\")\n\n        results = resp_body.get(\"results\", [])\n        # results not a list\n        if not isinstance(results, list):\n            logger.error(\"`results` in response is not a list, cUrl format: %s\", curl_format)\n            raise CustomAPIRequestFailed(\"the `results` in response is not a list\")\n\n        # currently, if the results is empty, CustomTypeList.custom_type will raise IndexError(task fail)\n        # so, here, we should check here: results size should not be empty\n        if not results:\n            logger.error(\"`results` in response is empty, cUrl format: %s\", curl_format)\n            raise CustomAPIRequestFailed(\"the `results` in response is empty\")\n\n        return results\n\n    def fetch_profiles(self, page_info: Optional[PageInfo] = None) -> CustomTypeList:\n        \"\"\"\u83b7\u53d6 profile \u5bf9\u8c61\u5217\u8868\"\"\"\n        results = self._fetch_items(path=self.paths[\"profile\"])\n        return CustomTypeList.from_list([CustomProfile.from_dict(x) for x in results])\n\n    def fetch_departments(self, page_info: Optional[PageInfo] = None) -> CustomTypeList:\n        \"\"\"\u83b7\u53d6 department \u5bf9\u8c61\u5217\u8868\"\"\"\n        results = self._fetch_items(path=self.paths[\"department\"])\n        return CustomTypeList.from_list([CustomDepartment.from_dict(x) for x in results])\n", "path": "src/api/bkuser_core/categories/plugins/custom/client.py"}]}