schema.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. from urllib.parse import urlparse
  2. from django.conf import settings
  3. from django.core.validators import URLValidator
  4. from django.urls import reverse
  5. from ninja import Field, ModelSchema
  6. from ninja.errors import ValidationError
  7. from pydantic import model_validator
  8. from glitchtip.schema import CamelSchema
  9. from .constants import HTTP_MONITOR_TYPES, MonitorType
  10. from .models import Monitor, MonitorCheck, StatusPage
  11. class MonitorCheckSchema(CamelSchema, ModelSchema):
  12. class Meta:
  13. model = MonitorCheck
  14. fields = ["is_up", "start_check", "reason"]
  15. class MonitorCheckResponseTimeSchema(MonitorCheckSchema, ModelSchema):
  16. """Monitor check with response time. Used in Monitors detail api and monitor checks list"""
  17. class Meta(MonitorCheckSchema.Meta):
  18. fields = MonitorCheckSchema.Meta.fields + ["response_time"]
  19. class MonitorIn(CamelSchema, ModelSchema):
  20. # project: int | None
  21. @model_validator(mode="after")
  22. def validate(self):
  23. monitor_type = self.monitor_type
  24. if self.url == "" and monitor_type in HTTP_MONITOR_TYPES + (MonitorType.SSL,):
  25. raise ValidationError("URL is required for " + monitor_type)
  26. if monitor_type in HTTP_MONITOR_TYPES:
  27. URLValidator()(self.url)
  28. if self.expected_status is None and monitor_type in [
  29. MonitorType.GET,
  30. MonitorType.POST,
  31. ]:
  32. raise ValidationError("Expected status is required for " + monitor_type)
  33. if monitor_type == MonitorType.PORT:
  34. url = self.url.replace("http://", "//", 1)
  35. if not url.startswith("//"):
  36. url = "//" + url
  37. parsed_url = urlparse(url)
  38. message = "Invalid Port URL, expected hostname and port"
  39. try:
  40. if not all([parsed_url.hostname, parsed_url.port]):
  41. raise ValidationError(message)
  42. except ValueError as err:
  43. raise ValidationError(message) from err
  44. self.url = f"{parsed_url.hostname}:{parsed_url.port}"
  45. return self
  46. class Meta:
  47. model = Monitor
  48. fields = [
  49. "monitor_type",
  50. "name",
  51. "url",
  52. "expected_status",
  53. "expected_body",
  54. "project",
  55. "interval",
  56. "timeout",
  57. ]
  58. class MonitorSchema(MonitorIn):
  59. project: int | None = Field(validation_alias="project_id")
  60. environment: int | None = Field(validation_alias="environment_id")
  61. is_up: bool | None = Field(validation_alias="latest_is_up")
  62. last_change: str | None
  63. heartbeat_endpoint: str | None
  64. project_name: str | None = None
  65. env_name: str | None = None
  66. checks: list[MonitorCheckSchema]
  67. organization: int = Field(validation_alias="organization_id")
  68. class Meta(MonitorIn.Meta):
  69. fields = [
  70. "id",
  71. "monitor_type",
  72. "endpoint_id",
  73. "created",
  74. "name",
  75. "url",
  76. "expected_status",
  77. "expected_body",
  78. "interval",
  79. "timeout",
  80. ]
  81. @staticmethod
  82. def resolve_last_change(obj):
  83. if obj.last_change:
  84. return obj.last_change.isoformat().replace("+00:00", "Z")
  85. @staticmethod
  86. def resolve_heartbeat_endpoint(obj):
  87. if obj.endpoint_id:
  88. return settings.GLITCHTIP_URL.geturl() + reverse(
  89. "heartbeat-check",
  90. kwargs={
  91. "organization_slug": obj.organization.slug,
  92. "endpoint_id": obj.endpoint_id,
  93. },
  94. )
  95. class MonitorDetailSchema(MonitorSchema):
  96. checks: list[MonitorCheckResponseTimeSchema]
  97. class StatusPageIn(CamelSchema, ModelSchema):
  98. is_public: bool = False
  99. class Meta:
  100. model = StatusPage
  101. fields = ["name", "is_public"]
  102. class StatusPageSchema(StatusPageIn, ModelSchema):
  103. monitors: list[MonitorSchema]
  104. class Meta(StatusPageIn.Meta):
  105. fields = ["name", "slug", "is_public"]