schema.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  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. @model_validator(mode="after")
  21. def validate(self):
  22. monitor_type = self.monitor_type
  23. if self.url == "" and monitor_type in HTTP_MONITOR_TYPES + (MonitorType.SSL,):
  24. raise ValidationError("URL is required for " + monitor_type)
  25. if monitor_type in HTTP_MONITOR_TYPES:
  26. URLValidator()(self.url)
  27. if self.expected_status is None and monitor_type in [
  28. MonitorType.GET,
  29. MonitorType.POST,
  30. ]:
  31. raise ValidationError("Expected status is required for " + monitor_type)
  32. if monitor_type == MonitorType.PORT:
  33. url = self.url.replace("http://", "//", 1)
  34. if not url.startswith("//"):
  35. url = "//" + url
  36. parsed_url = urlparse(url)
  37. message = "Invalid Port URL, expected hostname and port"
  38. try:
  39. if not all([parsed_url.hostname, parsed_url.port]):
  40. raise ValidationError(message)
  41. except ValueError as err:
  42. raise ValidationError(message) from err
  43. self.url = f"{parsed_url.hostname}:{parsed_url.port}"
  44. return self
  45. class Meta:
  46. model = Monitor
  47. fields = [
  48. "monitor_type",
  49. "name",
  50. "url",
  51. "expected_status",
  52. "expected_body",
  53. "project",
  54. "interval",
  55. "timeout",
  56. ]
  57. class MonitorSchema(MonitorIn, ModelSchema):
  58. project: int | None = Field(validation_alias="project_id")
  59. environment: int | None = Field(validation_alias="environment_id")
  60. is_up: bool | None = Field(validation_alias="latest_is_up")
  61. last_change: str | None
  62. heartbeat_endpoint: str | None
  63. project_name: str | None = None
  64. env_name: str | None = None
  65. checks: list[MonitorCheckSchema]
  66. organization: int = Field(validation_alias="organization_id")
  67. class Meta(MonitorIn.Meta):
  68. fields = [
  69. "id",
  70. "monitor_type",
  71. "endpoint_id",
  72. "created",
  73. "name",
  74. "url",
  75. "expected_status",
  76. "expected_body",
  77. "interval",
  78. "timeout",
  79. ]
  80. @staticmethod
  81. def resolve_last_change(obj):
  82. if obj.last_change:
  83. return obj.last_change.isoformat().replace("+00:00", "Z")
  84. @staticmethod
  85. def resolve_heartbeat_endpoint(obj):
  86. if obj.endpoint_id:
  87. return settings.GLITCHTIP_URL.geturl() + reverse(
  88. "api:heartbeat_check",
  89. kwargs={
  90. "organization_slug": obj.organization.slug,
  91. "endpoint_id": obj.endpoint_id,
  92. },
  93. )
  94. class MonitorDetailSchema(MonitorSchema):
  95. checks: list[MonitorCheckResponseTimeSchema]
  96. class StatusPageIn(CamelSchema, ModelSchema):
  97. is_public: bool = False
  98. class Meta:
  99. model = StatusPage
  100. fields = ["name", "is_public"]
  101. class StatusPageSchema(StatusPageIn, ModelSchema):
  102. monitors: list[MonitorSchema]
  103. class Meta(StatusPageIn.Meta):
  104. fields = ["name", "slug", "is_public"]