mirror of
https://git.ianrenton.com/ian/spothole.git
synced 2026-09-24 16:24:32 +00:00
First attempt at converting "sig" to "activity" for v3
This commit is contained in:
@@ -21,7 +21,7 @@ RECAPTCHA_VERIFY_URL = "https://www.google.com/recaptcha/api/siteverify"
|
||||
|
||||
|
||||
class APISpotHandler(tornado.web.RequestHandler):
|
||||
"""API request handler for /api/v2/spot (POST)"""
|
||||
"""API request handler for /api/v3/spot (POST)"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -144,17 +144,17 @@ class APISpotHandler(tornado.web.RequestHandler):
|
||||
|
||||
# Reject if activity ref format incorrect for activity
|
||||
if (
|
||||
spot.sig
|
||||
and spot.sig_refs
|
||||
and len(spot.sig_refs) > 0
|
||||
and spot.sig_refs[0].id
|
||||
and get_ref_regex_for_activity(spot.sig)
|
||||
and not re.match(get_ref_regex_for_activity(spot.sig), spot.sig_refs[0].id)
|
||||
spot.activity
|
||||
and spot.activity_refs
|
||||
and len(spot.activity_refs) > 0
|
||||
and spot.activity_refs[0].id
|
||||
and get_ref_regex_for_activity(spot.activity)
|
||||
and not re.match(get_ref_regex_for_activity(spot.activity), spot.activity_refs[0].id)
|
||||
):
|
||||
self.set_status(422)
|
||||
self.write(
|
||||
safe_json_dumps(
|
||||
f"Error - '{spot.sig_refs[0].id}' does not look like a valid reference for {spot.sig}."
|
||||
f"Error - '{spot.activity_refs[0].id}' does not look like a valid reference for {spot.activity}."
|
||||
)
|
||||
)
|
||||
self.set_header("Cache-Control", "no-store")
|
||||
@@ -171,13 +171,13 @@ class APISpotHandler(tornado.web.RequestHandler):
|
||||
|
||||
# Validate upstream submission requirements
|
||||
if submit_upstream and upstream_provider_name:
|
||||
if not spot.sig:
|
||||
if not spot.activity:
|
||||
self.set_status(422)
|
||||
self.write(safe_json_dumps("Error - an activity must be selected to submit upstream."))
|
||||
self.set_header("Cache-Control", "no-store")
|
||||
self.set_header("Content-Type", "application/json")
|
||||
return
|
||||
if not spot.sig_refs and upstream_provider_name != "Tiles":
|
||||
if not spot.activity_refs and upstream_provider_name != "Tiles":
|
||||
self.set_status(422)
|
||||
self.write(safe_json_dumps("Error - an activity reference is required to submit upstream."))
|
||||
self.set_header("Cache-Control", "no-store")
|
||||
@@ -201,7 +201,7 @@ class APISpotHandler(tornado.web.RequestHandler):
|
||||
# Submit upstream if requested
|
||||
upstream_warning = None
|
||||
if submit_upstream and upstream_provider_name:
|
||||
provider = self._find_provider(upstream_provider_name, spot.sig)
|
||||
provider = self._find_provider(upstream_provider_name, spot.activity)
|
||||
if provider:
|
||||
try:
|
||||
# Submit spot to the upstream provider
|
||||
@@ -216,7 +216,7 @@ class APISpotHandler(tornado.web.RequestHandler):
|
||||
f"Spot was saved locally but upstream submission to {upstream_provider_name} failed."
|
||||
)
|
||||
else:
|
||||
upstream_warning = f"No enabled provider named '{upstream_provider_name}' supports upstream submission for {spot.sig if spot.sig else ''} spots."
|
||||
upstream_warning = f"No enabled provider named '{upstream_provider_name}' supports upstream submission for {spot.activity if spot.activity else ''} spots."
|
||||
|
||||
# If we successfully submitted the spot upstream, don't add it direct to Spothole, otherwise it will be a
|
||||
# duplicate with what immediately comes back from the API. But if we weren't asked to send it upstream, or
|
||||
|
||||
@@ -17,7 +17,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class APIAlertsHandler(tornado.web.RequestHandler):
|
||||
"""API request handler for /api/v2/alerts"""
|
||||
"""API request handler for /api/v3/alerts"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -69,7 +69,7 @@ class APIAlertsHandler(tornado.web.RequestHandler):
|
||||
|
||||
|
||||
class APIAlertsStreamHandler(tornado_eventsource.handler.EventSourceHandler):
|
||||
"""API request handler for /api/v2/alerts/stream"""
|
||||
"""API request handler for /api/v3/alerts/stream"""
|
||||
|
||||
def __init__(self, application, request, **kwargs: Any):
|
||||
self._sse_alert_broadcaster = None
|
||||
@@ -169,13 +169,13 @@ def alert_allowed_by_query(alert, query):
|
||||
# the alert is a dxpedition, or contests_skip_max_duration_check and the alert is a contest, it also
|
||||
# always passes the check.
|
||||
if (
|
||||
alert.sig == ActivityName.DXPEDITION
|
||||
alert.activity == ActivityName.DXPEDITION
|
||||
and "dxpeditions_skip_max_duration_check" in query
|
||||
and query.get("dxpeditions_skip_max_duration_check").upper() == "TRUE"
|
||||
):
|
||||
continue
|
||||
if (
|
||||
alert.sig == ActivityName.CONTEST
|
||||
alert.activity == ActivityName.CONTEST
|
||||
and "contests_skip_max_duration_check" in query
|
||||
and query.get("contests_skip_max_duration_check").upper() == "TRUE"
|
||||
):
|
||||
@@ -186,14 +186,14 @@ def alert_allowed_by_query(alert, query):
|
||||
sources = query.get(k).split(",")
|
||||
if not alert.source or alert.source not in sources:
|
||||
return False
|
||||
case "sig":
|
||||
case "activity":
|
||||
# If a list of activities is provided, the alert must have an activity and it must match one of them.
|
||||
# The special activity "NO_SIG", when supplied in the list, matches alerts with no activity.
|
||||
# The special activity "NO_ACTIVITY", when supplied in the list, matches alerts with no activity.
|
||||
activities = query.get(k).split(",")
|
||||
include_no_activity = "NO_SIG" in activities
|
||||
if not alert.sig and not include_no_activity:
|
||||
include_no_activity = "NO_ACTIVITY" in activities
|
||||
if not alert.activity and not include_no_activity:
|
||||
return False
|
||||
if alert.sig and alert.sig not in activities:
|
||||
if alert.activity and alert.activity not in activities:
|
||||
return False
|
||||
case "dx_continent":
|
||||
dxconts = query.get(k).split(",")
|
||||
|
||||
@@ -20,7 +20,7 @@ HF_BANDS = [b.name for b in BANDS if b.is_ham_hf]
|
||||
|
||||
|
||||
class APIDxStatsHandler(tornado.web.RequestHandler):
|
||||
"""API request handler for /api/v2/dxstats"""
|
||||
"""API request handler for /api/v3/dxstats"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
||||
@@ -22,7 +22,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class APILookupCallHandler(tornado.web.RequestHandler):
|
||||
"""API request handler for /api/v2/lookup/call"""
|
||||
"""API request handler for /api/v3/lookup/call"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -63,7 +63,7 @@ class APILookupCallHandler(tornado.web.RequestHandler):
|
||||
|
||||
|
||||
class APILookupActivityRefHandler(tornado.web.RequestHandler):
|
||||
"""API request handler for /api/v2/lookup/sigref"""
|
||||
"""API request handler for /api/v3/lookup/activityref"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -79,16 +79,16 @@ class APILookupActivityRefHandler(tornado.web.RequestHandler):
|
||||
# reduce that to just the first entry, and convert bytes to string
|
||||
query_params = {k: v[0].decode("utf-8") for k, v in self.request.arguments.items()}
|
||||
|
||||
# "sig" and "id" query params must exist, the activity must be known, and if we have a reference regex for
|
||||
# "activity" and "id" query params must exist, the activity must be known, and if we have a reference regex for
|
||||
# that activity, the provided id must match it.
|
||||
if "sig" in query_params and "id" in query_params:
|
||||
activity = str(query_params.get("sig")).upper()
|
||||
if "activity" in query_params and "id" in query_params:
|
||||
activity = str(query_params.get("activity")).upper()
|
||||
ref_id = str(query_params.get("id")).upper()
|
||||
if get_activity_by_name(activity):
|
||||
if not get_ref_regex_for_activity(activity) or re.match(
|
||||
get_ref_regex_for_activity(activity), ref_id
|
||||
):
|
||||
data = populate_missing_activity_ref_info(ActivityRef(id=ref_id, sig=activity))
|
||||
data = populate_missing_activity_ref_info(ActivityRef(id=ref_id, activity=activity))
|
||||
self.write(safe_json_dumps(data))
|
||||
|
||||
else:
|
||||
@@ -99,10 +99,10 @@ class APILookupActivityRefHandler(tornado.web.RequestHandler):
|
||||
)
|
||||
self.set_status(422)
|
||||
else:
|
||||
self.write(safe_json_dumps(f"Error - sig '{activity}' is not known."))
|
||||
self.write(safe_json_dumps(f"Error - activity '{activity}' is not known."))
|
||||
self.set_status(422)
|
||||
else:
|
||||
self.write(safe_json_dumps("Error - sig and id must be provided"))
|
||||
self.write(safe_json_dumps("Error - activity and id must be provided"))
|
||||
self.set_status(422)
|
||||
|
||||
except Exception:
|
||||
@@ -115,7 +115,7 @@ class APILookupActivityRefHandler(tornado.web.RequestHandler):
|
||||
|
||||
|
||||
class APILookupGridHandler(tornado.web.RequestHandler):
|
||||
"""API request handler for /api/v2/lookup/grid"""
|
||||
"""API request handler for /api/v3/lookup/grid"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
||||
@@ -15,7 +15,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class APIOptionsHandler(tornado.web.RequestHandler):
|
||||
"""API request handler for /api/v2/options"""
|
||||
"""API request handler for /api/v3/options"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -75,7 +75,7 @@ class APIOptionsHandler(tornado.web.RequestHandler):
|
||||
"bands": BANDS,
|
||||
"modes": [m.value for m in Mode],
|
||||
"mode_types": [t.value for t in ModeType],
|
||||
"sigs": list(ACTIVITIES.values()),
|
||||
"activities": list(ACTIVITIES.values()),
|
||||
"spot_providers": spot_providers,
|
||||
"spot_providers_enabled_by_default": spot_providers_enabled_by_default,
|
||||
"alert_providers": alert_providers,
|
||||
|
||||
@@ -11,7 +11,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class APISolarConditionsHandler(tornado.web.RequestHandler):
|
||||
"""API request handler for /api/v2/solar"""
|
||||
"""API request handler for /api/v3/solar"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
||||
@@ -16,7 +16,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class APISpotsHandler(tornado.web.RequestHandler):
|
||||
"""API request handler for /api/v2/spots"""
|
||||
"""API request handler for /api/v3/spots"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -68,7 +68,7 @@ class APISpotsHandler(tornado.web.RequestHandler):
|
||||
|
||||
|
||||
class APISpotsStreamHandler(tornado_eventsource.handler.EventSourceHandler):
|
||||
"""API request handler for /api/v2/spots/stream"""
|
||||
"""API request handler for /api/v3/spots/stream"""
|
||||
|
||||
def __init__(self, application, request, **kwargs: Any):
|
||||
self._sse_spot_broadcaster = None
|
||||
@@ -196,25 +196,25 @@ def spot_allowed_by_query(spot, query):
|
||||
sources = query.get(k).split(",")
|
||||
if not spot.source or spot.source not in sources:
|
||||
return False
|
||||
case "sig":
|
||||
case "activity":
|
||||
# If a list of activities is provided, the spot must have an activity and it must match one of them.
|
||||
# The special activity "NO_SIG", when supplied in the list, matches spots with no activity.
|
||||
# The special activity "NO_ACTIVITY", when supplied in the list, matches spots with no activity.
|
||||
activities = query.get(k).split(",")
|
||||
include_no_activity = "NO_SIG" in activities
|
||||
if not spot.sig and not include_no_activity:
|
||||
include_no_activity = "NO_ACTIVITY" in activities
|
||||
if not spot.activity and not include_no_activity:
|
||||
return False
|
||||
if spot.sig and spot.sig not in activities:
|
||||
if spot.activity and spot.activity not in activities:
|
||||
return False
|
||||
case "needs_sig":
|
||||
case "needs_activity":
|
||||
# If true, an activity is required, regardless of what it is, it just can't be missing. Mutually
|
||||
# exclusive with supplying the special "NO_SIG" parameter to the "sig" query param.
|
||||
# exclusive with supplying the special "NO_ACTIVITY" parameter to the "activity" query param.
|
||||
needs_activity = query.get(k).upper() == "TRUE"
|
||||
if needs_activity and not spot.sig:
|
||||
if needs_activity and not spot.activity:
|
||||
return False
|
||||
case "needs_sig_ref":
|
||||
case "needs_activity_ref":
|
||||
# If true, at least one activity ref is required, regardless of what it is, it just can't be missing.
|
||||
needs_activity_ref = query.get(k).upper() == "TRUE"
|
||||
if needs_activity_ref and (not spot.sig_refs or len(spot.sig_refs) == 0):
|
||||
if needs_activity_ref and (not spot.activity_refs or len(spot.activity_refs) == 0):
|
||||
return False
|
||||
case "band":
|
||||
bands = query.get(k).split(",")
|
||||
|
||||
@@ -11,7 +11,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class APIStatusHandler(tornado.web.RequestHandler):
|
||||
"""API request handler for /api/v2/status"""
|
||||
"""API request handler for /api/v3/status"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
||||
@@ -11,12 +11,14 @@ from core.config import ALLOW_SPOTTING
|
||||
from core.constants import UNKNOWN_BAND
|
||||
from core.utils import infer_band_from_freq, safe_json_dumps
|
||||
from data.spot import Spot
|
||||
from webserver.handlers.api.v2_compatibility import translate_v2_spot
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class V1APISpotHandler(tornado.web.RequestHandler):
|
||||
"""API request handler for /api/v1/spot (POST). Included in early Spothole v2 for backwards compatibility."""
|
||||
"""API request handler for /api/v1/spot (POST). Included in Spothole v2 onwards for backwards
|
||||
compatibility."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -57,8 +59,9 @@ class V1APISpotHandler(tornado.web.RequestHandler):
|
||||
self.set_header("Content-Type", "application/json")
|
||||
return
|
||||
|
||||
# Read in the request body as JSON then convert to a Spot object
|
||||
json_spot = tornado.escape.json_decode(post_data)
|
||||
# Read in the request body as JSON then convert to a Spot object. The v1 spot format uses the same field
|
||||
# names as v2, so needs the same translation to v3 field names.
|
||||
json_spot = translate_v2_spot(tornado.escape.json_decode(post_data))
|
||||
spot = Spot(**json_spot)
|
||||
|
||||
# Reject if no timestamp, frequency, dx_call or de_call
|
||||
@@ -106,17 +109,17 @@ class V1APISpotHandler(tornado.web.RequestHandler):
|
||||
|
||||
# Reject if activity ref format incorrect for activity
|
||||
if (
|
||||
spot.sig
|
||||
and spot.sig_refs
|
||||
and len(spot.sig_refs) > 0
|
||||
and spot.sig_refs[0].id
|
||||
and get_ref_regex_for_activity(spot.sig)
|
||||
and not re.match(get_ref_regex_for_activity(spot.sig), spot.sig_refs[0].id)
|
||||
spot.activity
|
||||
and spot.activity_refs
|
||||
and len(spot.activity_refs) > 0
|
||||
and spot.activity_refs[0].id
|
||||
and get_ref_regex_for_activity(spot.activity)
|
||||
and not re.match(get_ref_regex_for_activity(spot.activity), spot.activity_refs[0].id)
|
||||
):
|
||||
self.set_status(422)
|
||||
self.write(
|
||||
safe_json_dumps(
|
||||
f"Error - '{spot.sig_refs[0].id}' does not look like a valid reference for {spot.sig}."
|
||||
f"Error - '{spot.activity_refs[0].id}' does not look like a valid reference for {spot.activity}."
|
||||
)
|
||||
)
|
||||
self.set_header("Cache-Control", "no-store")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import re
|
||||
|
||||
from webserver.handlers.api.spots import APISpotsHandler, APISpotsStreamHandler
|
||||
from webserver.handlers.api.v2_compatibility import V2APISpotsHandler, V2APISpotsStreamHandler
|
||||
|
||||
_GRID_SOURCE_RE = re.compile(r'"dx_location_source":\s*"GRID"')
|
||||
_LEGACY_PARAM_TO_HEADER_MAP = {
|
||||
@@ -24,8 +24,9 @@ def _handle_legacy_params(handler):
|
||||
handler.request.headers[header] = value
|
||||
|
||||
|
||||
class V1APISpotsHandler(APISpotsHandler):
|
||||
"""API request handler for /api/v1/spots (GET). Included in early Spothole v2 for backwards compatibility."""
|
||||
class V1APISpotsHandler(V2APISpotsHandler):
|
||||
"""API request handler for /api/v1/spots (GET). Included in Spothole v2 onwards for backwards
|
||||
compatibility."""
|
||||
|
||||
def prepare(self):
|
||||
_handle_legacy_params(self)
|
||||
@@ -37,8 +38,9 @@ class V1APISpotsHandler(APISpotsHandler):
|
||||
super().write(chunk)
|
||||
|
||||
|
||||
class V1APISpotsStreamHandler(APISpotsStreamHandler):
|
||||
"""API request handler for /api/v1/spots/stream (SSE). Included in early Spothole v2 for backwards compatibility."""
|
||||
class V1APISpotsStreamHandler(V2APISpotsStreamHandler):
|
||||
"""API request handler for /api/v1/spots/stream (SSE). Included in Spothole v2 onwards for backwards
|
||||
compatibility."""
|
||||
|
||||
def prepare(self):
|
||||
_handle_legacy_params(self)
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
import json
|
||||
|
||||
import tornado
|
||||
|
||||
from core.utils import safe_json_dumps
|
||||
from webserver.handlers.api.addspot import APISpotHandler
|
||||
from webserver.handlers.api.alerts import APIAlertsHandler, APIAlertsStreamHandler
|
||||
from webserver.handlers.api.lookups import APILookupActivityRefHandler
|
||||
from webserver.handlers.api.options import APIOptionsHandler
|
||||
from webserver.handlers.api.spots import APISpotsHandler, APISpotsStreamHandler
|
||||
from webserver.handlers.api.status import APIStatusHandler
|
||||
|
||||
# Query parameters renamed in v3
|
||||
_V2_TO_V3_QUERY_PARAMS = {
|
||||
"sig": "activity",
|
||||
"needs_sig": "needs_activity",
|
||||
"needs_sig_ref": "needs_activity_ref",
|
||||
}
|
||||
# Values of query parameters renamed in v3
|
||||
_V2_TO_V3_QUERY_VALUES = {
|
||||
"activity": {"NO_SIG": "NO_ACTIVITY"},
|
||||
"fields": {"sig": "activity", "sig_refs": "activity_refs"},
|
||||
}
|
||||
# Keys of JSON objects in API responses renamed in v3
|
||||
_V3_TO_V2_RESPONSE_KEYS = {
|
||||
"activity": "sig",
|
||||
"activity_refs": "sig_refs",
|
||||
"activity_type": "sig_type",
|
||||
"activities": "sigs",
|
||||
"activity_ref_data_providers": "sig_ref_data_providers",
|
||||
"activity_name": "sig_name",
|
||||
}
|
||||
# Values of JSON objects in API responses renamed in v3
|
||||
_V3_TO_V2_RESPONSE_VALUES = {
|
||||
"dx_location_source": {"ACTIVITY REF LOOKUP": "SIG REF LOOKUP"},
|
||||
}
|
||||
|
||||
|
||||
def _translate_v2_query_params(handler):
|
||||
"""Rename any v2 query parameters (and values) in the request to their v3 equivalents, so the v3 handler can
|
||||
understand them."""
|
||||
|
||||
for arguments in (handler.request.arguments, handler.request.query_arguments):
|
||||
for v2_name, v3_name in _V2_TO_V3_QUERY_PARAMS.items():
|
||||
if v2_name in arguments:
|
||||
arguments[v3_name] = arguments.pop(v2_name)
|
||||
for name, value_map in _V2_TO_V3_QUERY_VALUES.items():
|
||||
if name in arguments:
|
||||
arguments[name] = [
|
||||
",".join(value_map.get(item.strip(), item) for item in v.decode("utf-8").split(",")).encode("utf-8")
|
||||
for v in arguments[name]
|
||||
]
|
||||
|
||||
|
||||
def _translate_v3_response_object(obj):
|
||||
"""Rename keys and values in an object from their v3 names to their v2 names."""
|
||||
|
||||
if isinstance(obj, dict):
|
||||
translated = {}
|
||||
for k, v in obj.items():
|
||||
if k in _V3_TO_V2_RESPONSE_VALUES and isinstance(v, str):
|
||||
v = _V3_TO_V2_RESPONSE_VALUES[k].get(v, v)
|
||||
translated[_V3_TO_V2_RESPONSE_KEYS.get(k, k)] = _translate_v3_response_object(v)
|
||||
return translated
|
||||
if isinstance(obj, list):
|
||||
return [_translate_v3_response_object(i) for i in obj]
|
||||
return obj
|
||||
|
||||
|
||||
def translate_v3_response(chunk):
|
||||
"""Translate a JSON string output by a v3 handler into its v2 equivalent"""
|
||||
|
||||
if not isinstance(chunk, str):
|
||||
return chunk
|
||||
try:
|
||||
return safe_json_dumps(_translate_v3_response_object(json.loads(chunk)))
|
||||
except ValueError:
|
||||
return chunk
|
||||
|
||||
|
||||
def translate_v2_spot(spot_data):
|
||||
"""Translate a spot provided by a client in v2 format into v3 format"""
|
||||
|
||||
spot_data = dict(spot_data)
|
||||
if "sig" in spot_data:
|
||||
spot_data["activity"] = spot_data.pop("sig")
|
||||
if "sig_refs" in spot_data:
|
||||
spot_data["activity_refs"] = spot_data.pop("sig_refs")
|
||||
if isinstance(spot_data.get("activity_refs"), list):
|
||||
refs = []
|
||||
for ref in spot_data["activity_refs"]:
|
||||
if isinstance(ref, dict) and "sig" in ref:
|
||||
ref = dict(ref)
|
||||
ref["activity"] = ref.pop("sig")
|
||||
refs.append(ref)
|
||||
spot_data["activity_refs"] = refs
|
||||
return spot_data
|
||||
|
||||
|
||||
class _V2ResponseTranslationMixin:
|
||||
"""Mixin for request handlers that translates v2 query params to v3 on the way in, and v3 JSON responses to v2 on
|
||||
the way out"""
|
||||
|
||||
def prepare(self):
|
||||
_translate_v2_query_params(self)
|
||||
super().prepare()
|
||||
|
||||
def write(self, chunk):
|
||||
super().write(translate_v3_response(chunk))
|
||||
|
||||
|
||||
class _V2StreamTranslationMixin:
|
||||
"""Mixin for SSE handlers that translates v2 query params to v3 on the way in, and v3 JSON messages to v2 on the way
|
||||
out"""
|
||||
|
||||
def prepare(self):
|
||||
_translate_v2_query_params(self)
|
||||
super().prepare()
|
||||
|
||||
def write_message(self, name=None, msg=True, wait=None, evt_id=None):
|
||||
if not name:
|
||||
msg = translate_v3_response(msg)
|
||||
return super().write_message(name=name, msg=msg, wait=wait, evt_id=evt_id)
|
||||
|
||||
|
||||
class V2APISpotsHandler(_V2ResponseTranslationMixin, APISpotsHandler):
|
||||
"""API request handler for /api/v2/spots (GET). Included in Spothole v3 for backwards compatibility."""
|
||||
|
||||
|
||||
class V2APISpotsStreamHandler(_V2StreamTranslationMixin, APISpotsStreamHandler):
|
||||
"""API request handler for /api/v2/spots/stream (SSE). Included in Spothole v3 for backwards compatibility."""
|
||||
|
||||
|
||||
class V2APIAlertsHandler(_V2ResponseTranslationMixin, APIAlertsHandler):
|
||||
"""API request handler for /api/v2/alerts (GET). Included in Spothole v3 for backwards compatibility."""
|
||||
|
||||
|
||||
class V2APIAlertsStreamHandler(_V2StreamTranslationMixin, APIAlertsStreamHandler):
|
||||
"""API request handler for /api/v2/alerts/stream (SSE). Included in Spothole v3 for backwards compatibility."""
|
||||
|
||||
|
||||
class V2APIOptionsHandler(_V2ResponseTranslationMixin, APIOptionsHandler):
|
||||
"""API request handler for /api/v2/options (GET). Included in Spothole v3 for backwards compatibility."""
|
||||
|
||||
|
||||
class V2APIStatusHandler(_V2ResponseTranslationMixin, APIStatusHandler):
|
||||
"""API request handler for /api/v2/status (GET). Included in Spothole v3 for backwards compatibility."""
|
||||
|
||||
|
||||
class V2APILookupSigRefHandler(_V2ResponseTranslationMixin, APILookupActivityRefHandler):
|
||||
"""API request handler for /api/v2/lookup/sigref (GET). Included in Spothole v3 for backwards compatibility. This
|
||||
is the v2 equivalent of /api/v3/lookup/activityref."""
|
||||
|
||||
|
||||
class V2APISpotHandler(APISpotHandler):
|
||||
"""API request handler for /api/v2/spot (POST). Included in Spothole v3 for backwards compatibility. Translates the
|
||||
spot in the request body from v2 to v3 format. The response is a plain status message so needs no translation."""
|
||||
|
||||
def post(self):
|
||||
# Translate the request body if we can. If the body is empty or invalid JSON, leave it alone and let the v3
|
||||
# handler return the appropriate error.
|
||||
try:
|
||||
json_body = tornado.escape.json_decode(self.request.body)
|
||||
if isinstance(json_body, dict) and isinstance(json_body.get("spot"), dict):
|
||||
json_body["spot"] = translate_v2_spot(json_body["spot"])
|
||||
self.request.body = json.dumps(json_body).encode("utf-8")
|
||||
except ValueError:
|
||||
pass
|
||||
super().post()
|
||||
Reference in New Issue
Block a user