Skip to content

Commit 9dcb897

Browse files
authored
Merge branch 'main' into dss0210-sub-manager-synced
2 parents 7dbf130 + 46c020a commit 9dcb897

23 files changed

+1303
-39
lines changed

monitoring/monitorlib/mutate/scd.py

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@
44
import s2sphere
55
import yaml
66
from implicitdict import ImplicitDict
7-
from uas_standards.astm.f3548.v21.api import OPERATIONS, OperationID, Subscription
7+
from uas_standards.astm.f3548.v21.api import (
8+
OPERATIONS,
9+
OperationID,
10+
Subscription,
11+
PutSubscriptionParameters,
12+
)
813
from yaml.representer import Representer
914

1015
from monitoring.monitorlib import fetch
@@ -74,18 +79,16 @@ def upsert_subscription(
7479
path = op.path.format(subscriptionid=subscription_id, version=version)
7580
query_type = QueryType.F3548v21DSSUpdateSubscription
7681

77-
body = {
78-
"extents": Volume4D.from_values(
79-
start_time,
80-
end_time,
81-
min_alt_m,
82-
max_alt_m,
83-
polygon=Polygon.from_latlng_rect(latlngrect=area),
84-
).to_f3548v21(),
85-
"uss_base_url": base_url,
86-
"notify_for_operational_intents": notify_for_op_intents,
87-
"notify_for_constraints": notify_for_constraints,
88-
}
82+
body = build_upsert_subscription_params(
83+
area_vertices=area,
84+
start_time=start_time,
85+
end_time=end_time,
86+
base_url=base_url,
87+
notify_for_op_intents=notify_for_op_intents,
88+
notify_for_constraints=notify_for_constraints,
89+
min_alt_m=min_alt_m,
90+
max_alt_m=max_alt_m,
91+
)
8992

9093
result = MutatedSubscription(
9194
fetch.query_and_describe(
@@ -102,6 +105,30 @@ def upsert_subscription(
102105
return result
103106

104107

108+
def build_upsert_subscription_params(
109+
area_vertices: s2sphere.LatLngRect,
110+
start_time: datetime.datetime,
111+
end_time: datetime.datetime,
112+
base_url: str,
113+
notify_for_op_intents: bool,
114+
notify_for_constraints: bool,
115+
min_alt_m: float,
116+
max_alt_m: float,
117+
) -> PutSubscriptionParameters:
118+
return PutSubscriptionParameters(
119+
extents=Volume4D.from_values(
120+
start_time,
121+
end_time,
122+
min_alt_m,
123+
max_alt_m,
124+
polygon=Polygon.from_latlng_rect(latlngrect=area_vertices),
125+
).to_f3548v21(),
126+
uss_base_url=base_url,
127+
notify_for_operational_intents=notify_for_op_intents,
128+
notify_for_constraints=notify_for_constraints,
129+
)
130+
131+
105132
def delete_subscription(
106133
utm_client: infrastructure.UTMClientSession,
107134
subscription_id: str,

monitoring/monitorlib/schema_validation.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class F3411_22a(str, Enum):
5050

5151
class F3548_21(str, Enum):
5252
OpenAPIPath = "interfaces/astm-utm/Protocol/utm.yaml"
53+
ErrorResponse = "components.schemas.ErrorResponse"
5354
GetOperationalIntentDetailsResponse = (
5455
"components.schemas.GetOperationalIntentDetailsResponse"
5556
)

monitoring/uss_qualifier/configurations/dev/f3548_self_contained.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ v1:
4949
# ASTM F3548-21 USS emulation roles
5050
- utm.strategic_coordination
5151
- utm.availability_arbitration
52+
# For authentication test purposes.
53+
# Remove if the authentication provider pointed to by AUTH_SPEC does not support it.
54+
- ""
5255

5356
# Means by which uss_qualifier can discover which subscription ('sub' claim of its tokes) it is described by
5457
utm_client_identity:

monitoring/uss_qualifier/configurations/dev/library/environment_containers.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ utm_auth:
2929
- utm.availability_arbitration
3030
# InterUSS versioning automated testing
3131
- interuss.versioning.read_system_versions
32+
# For authentication test purposes.
33+
# Remove if the authentication provider pointed to by AUTH_SPEC does not support it.
34+
- ""
3235

3336
second_utm_auth:
3437
$content_schema: monitoring/uss_qualifier/resources/definitions/ResourceDeclaration.json

monitoring/uss_qualifier/configurations/dev/library/environment_localhost.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ utm_auth:
2929
- utm.availability_arbitration
3030
# InterUSS versioning automated testing
3131
- interuss.versioning.read_system_versions
32+
# For authentication test purposes
33+
- ""
3234

3335
second_utm_auth:
3436
$content_schema: monitoring/uss_qualifier/resources/definitions/ResourceDeclaration.json

monitoring/uss_qualifier/resources/astm/f3548/v21/dss.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
OperationID,
3131
GetOperationalIntentTelemetryResponse,
3232
VehicleTelemetry,
33+
ExchangeRecord,
34+
ErrorReport,
3335
)
3436
from uas_standards.astm.f3548.v21.constants import Scope
3537

@@ -104,6 +106,16 @@ def _uses_scope(self, *scopes: Tuple[str]) -> None:
104106
f"{fullname(type(self))} client called {calling_function_name(1)} which requires the use of the scope `{scope}`, but this DSSInstance is only authorized to perform actions with the scopes {' or '.join(self._scopes_authorized)}"
105107
)
106108

109+
def _uses_any_scope(self, *scopes: str) -> str:
110+
"""Validates that at least a required scope is authorized for a request.
111+
Additionally, returns a valid scope that may be used for the request."""
112+
for scope in scopes:
113+
if scope in self._scopes_authorized:
114+
return scope
115+
raise ValueError(
116+
f"{fullname(type(self))} client called {calling_function_name(1)} which requires the use of any of the scopes `{', '.join(scopes)}`, but this DSSInstance is only authorized to perform actions with the scopes {' or '.join(self._scopes_authorized)}"
117+
)
118+
107119
def can_use_scope(self, scope: str) -> bool:
108120
return scope in self._scopes_authorized
109121

@@ -369,6 +381,58 @@ def set_uss_availability(
369381
result = query.parse_json_result(UssAvailabilityStatusResponse)
370382
return result.version, query
371383

384+
def make_report(
385+
self,
386+
exchange: ExchangeRecord,
387+
) -> Tuple[Optional[str], Query]:
388+
"""
389+
Make a DSS report.
390+
Returns:
391+
A tuple composed of
392+
1) the report ID;
393+
2) the query.
394+
Raises:
395+
* QueryError: if request failed, if HTTP status code is different than 201, or if the parsing of the response failed.
396+
"""
397+
use_scope = self._uses_any_scope(
398+
Scope.ConstraintManagement,
399+
Scope.ConstraintProcessing,
400+
Scope.StrategicCoordination,
401+
Scope.ConformanceMonitoringForSituationalAwareness,
402+
Scope.AvailabilityArbitration,
403+
)
404+
405+
req = ErrorReport(exchange=exchange)
406+
op = OPERATIONS[OperationID.MakeDssReport]
407+
query = query_and_describe(
408+
self.client,
409+
op.verb,
410+
op.path,
411+
QueryType.F3548v21DSSMakeDssReport,
412+
self.participant_id,
413+
scope=use_scope,
414+
json=req,
415+
)
416+
417+
# TODO: this is a temporary hack: the endpoint is currently not implemented in the DSS, as such we expect the
418+
# DSS to respond with a 400 and a specific error message. This must be updated once this endpoint is actually
419+
# implemented in the DSS.
420+
# if query.status_code != 201:
421+
# raise QueryError(
422+
# f"Received code {query.status_code} when attempting to make DSS report{f'; error message: `{query.error_message}`' if query.error_message is not None else ''}",
423+
# query,
424+
# )
425+
# else:
426+
# result = query.parse_json_result(ErrorReport)
427+
# return result.report_id, query
428+
if query.status_code != 400 or "Not yet implemented" not in query.error_message:
429+
raise QueryError(
430+
f"Received code {query.status_code} when attempting to make DSS report{f'; error message: `{query.error_message}`' if query.error_message is not None else ''}",
431+
query,
432+
)
433+
else:
434+
return "dummy_report_id", query
435+
372436
def query_subscriptions(
373437
self,
374438
volume: Volume4D,
@@ -446,6 +510,9 @@ def __init__(
446510
def can_use_scope(self, scope: str) -> bool:
447511
return scope in self._auth_adapter.scopes
448512

513+
def get_authorized_scopes(self) -> Set[str]:
514+
return self._auth_adapter.scopes.copy()
515+
449516
def get_instance(self, scopes_required: Dict[str, str]) -> DSSInstance:
450517
"""Get a client object ready to be used.
451518

monitoring/uss_qualifier/resources/astm/f3548/v21/planning_area.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,16 +68,18 @@ def get_new_operational_intent_ref_params(
6868
uss_base_url: UssBaseURL,
6969
time_start: datetime.datetime,
7070
time_end: datetime.datetime,
71-
subscription_id: Optional[EntityID] = None,
71+
subscription_id: Optional[EntityID],
7272
implicit_sub_base_url: Optional[UssBaseURL] = None,
7373
implicit_sub_for_constraints: Optional[bool] = None,
7474
) -> PutOperationalIntentReferenceParameters:
7575
"""
7676
Builds a PutOperationalIntentReferenceParameters object that can be used against the DSS OIR API.
77+
7778
The extents contained in these parameters contain a single 4DVolume, which may not be entirely realistic,
7879
but is sufficient in situations where the content of the OIR is irrelevant as long as it is valid, such
7980
as for testing authentication or parameter validation.
80-
Note that this method allows building inconsistent parameters.
81+
82+
Note that this method allows building inconsistent parameters:
8183
"""
8284
return PutOperationalIntentReferenceParameters(
8385
extents=[

monitoring/uss_qualifier/resources/astm/f3548/v21/subscription_params.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import datetime
22
from typing import List, Optional, Self
33

4+
import s2sphere
45
from implicitdict import ImplicitDict
6+
from uas_standards.astm.f3548.v21.api import PutSubscriptionParameters
57

68
from monitoring.monitorlib.geo import LatLngPoint
9+
from monitoring.monitorlib.mutate import scd as mutate
710

811

912
class SubscriptionParams(ImplicitDict):
@@ -44,3 +47,28 @@ class SubscriptionParams(ImplicitDict):
4447

4548
def copy(self) -> Self:
4649
return SubscriptionParams(super().copy())
50+
51+
def to_upsert_subscription_params(
52+
self, area: s2sphere.LatLngRect
53+
) -> PutSubscriptionParameters:
54+
"""
55+
Prepares the subscription parameters to be used in the body of an HTTP request
56+
to create or update a subscription on the DSS in the SCD context.
57+
58+
Args:
59+
area: area to include in the subscription parameters
60+
61+
Returns:
62+
A dict to be passed as the request body when calling the subscription creation or update API.
63+
64+
"""
65+
return mutate.build_upsert_subscription_params(
66+
area_vertices=area,
67+
start_time=self.start_time,
68+
end_time=self.end_time,
69+
base_url=self.base_url,
70+
notify_for_op_intents=self.notify_for_op_intents,
71+
notify_for_constraints=self.notify_for_constraints,
72+
min_alt_m=self.min_alt_m,
73+
max_alt_m=self.max_alt_m,
74+
)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .authentication_validation import AuthenticationValidation

0 commit comments

Comments
 (0)