api_gateway.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. from datetime import datetime
  2. from http import HTTPStatus
  3. from typing import Optional
  4. import inject
  5. from clean_python import DoesNotExist
  6. from clean_python import Gateway
  7. from clean_python import Id
  8. from clean_python import Json
  9. from clean_python import Mapper
  10. from clean_python import SyncGateway
  11. from .api_provider import ApiProvider
  12. from .exceptions import ApiException
  13. from .sync_api_provider import SyncApiProvider
  14. __all__ = ["ApiGateway", "SyncApiGateway"]
  15. class ApiGateway(Gateway):
  16. path: str
  17. mapper = Mapper()
  18. def __init__(self, provider_override: Optional[ApiProvider] = None):
  19. self.provider_override = provider_override
  20. def __init_subclass__(cls, path: str) -> None:
  21. assert not path.startswith("/")
  22. assert "{id}" in path
  23. cls.path = path
  24. super().__init_subclass__()
  25. @property
  26. def provider(self) -> ApiProvider:
  27. return self.provider_override or inject.instance(ApiProvider)
  28. async def get(self, id: Id) -> Optional[Json]:
  29. try:
  30. result = await self.provider.request("GET", self.path.format(id=id))
  31. assert result is not None
  32. return self.mapper.to_internal(result)
  33. except ApiException as e:
  34. if e.status is HTTPStatus.NOT_FOUND:
  35. return None
  36. raise e
  37. async def add(self, item: Json) -> Json:
  38. item = self.mapper.to_external(item)
  39. result = await self.provider.request("POST", self.path.format(id=""), json=item)
  40. assert result is not None
  41. return self.mapper.to_internal(result)
  42. async def remove(self, id: Id) -> bool:
  43. try:
  44. await self.provider.request("DELETE", self.path.format(id=id)) is not None
  45. except ApiException as e:
  46. if e.status is HTTPStatus.NOT_FOUND:
  47. return False
  48. raise e
  49. else:
  50. return True
  51. async def update(
  52. self, item: Json, if_unmodified_since: Optional[datetime] = None
  53. ) -> Json:
  54. if if_unmodified_since is not None:
  55. raise NotImplementedError("if_unmodified_since not implemented")
  56. item = self.mapper.to_external(item)
  57. id_ = item.pop("id", None)
  58. if id_ is None:
  59. raise DoesNotExist("resource", id_)
  60. try:
  61. result = await self.provider.request(
  62. "PATCH", self.path.format(id=id_), json=item
  63. )
  64. assert result is not None
  65. return self.mapper.to_internal(result)
  66. except ApiException as e:
  67. if e.status is HTTPStatus.NOT_FOUND:
  68. raise DoesNotExist("resource", id_)
  69. raise e
  70. # This is a copy-paste of ApiGateway:
  71. class SyncApiGateway(SyncGateway):
  72. path: str
  73. mapper = Mapper()
  74. def __init__(self, provider_override: Optional[SyncApiProvider] = None):
  75. self.provider_override = provider_override
  76. def __init_subclass__(cls, path: str) -> None:
  77. assert not path.startswith("/")
  78. assert "{id}" in path
  79. cls.path = path
  80. super().__init_subclass__()
  81. @property
  82. def provider(self) -> SyncApiProvider:
  83. return self.provider_override or inject.instance(SyncApiProvider)
  84. def get(self, id: Id) -> Optional[Json]:
  85. try:
  86. result = self.provider.request("GET", self.path.format(id=id))
  87. assert result is not None
  88. return self.mapper.to_internal(result)
  89. except ApiException as e:
  90. if e.status is HTTPStatus.NOT_FOUND:
  91. return None
  92. raise e
  93. def add(self, item: Json) -> Json:
  94. item = self.mapper.to_external(item)
  95. result = self.provider.request("POST", self.path.format(id=""), json=item)
  96. assert result is not None
  97. return self.mapper.to_internal(result)
  98. def remove(self, id: Id) -> bool:
  99. try:
  100. self.provider.request("DELETE", self.path.format(id=id)) is not None
  101. except ApiException as e:
  102. if e.status is HTTPStatus.NOT_FOUND:
  103. return False
  104. raise e
  105. else:
  106. return True
  107. def update(
  108. self, item: Json, if_unmodified_since: Optional[datetime] = None
  109. ) -> Json:
  110. if if_unmodified_since is not None:
  111. raise NotImplementedError("if_unmodified_since not implemented")
  112. item = self.mapper.to_external(item)
  113. id_ = item.pop("id", None)
  114. if id_ is None:
  115. raise DoesNotExist("resource", id_)
  116. try:
  117. result = self.provider.request("PATCH", self.path.format(id=id_), json=item)
  118. assert result is not None
  119. return self.mapper.to_internal(result)
  120. except ApiException as e:
  121. if e.status is HTTPStatus.NOT_FOUND:
  122. raise DoesNotExist("resource", id_)
  123. raise e