@@ -37,12 +37,14 @@ def some_event_function():
3737```
3838"""
3939
40+ from typing import List
4041import logging
41- from typing import Dict
42+ from typing import Dict , Optional
4243
4344from ops .charm import CharmBase , RelationCreatedEvent
4445from ops .framework import EventBase , EventSource , Object , ObjectEvents
45- from ops .model import TooManyRelatedAppsError
46+ from ops import Relation , ModelError
47+ from pydantic import BaseModel
4648
4749# The unique Charmhub library identifier, never change it
4850LIBID = "f59057701b5840849d3cea756af404c6"
@@ -52,23 +54,24 @@ def some_event_function():
5254
5355# Increment this PATCH version before using `charmcraft publish-lib` or reset
5456# to 0 if you are raising the major API version
55- LIBPATCH = 2
57+ LIBPATCH = 3
5658
5759RELATION_NAME = "ui-endpoint-info"
5860INTERFACE_NAME = "login_ui_endpoints"
5961logger = logging .getLogger (__name__ )
6062
61- RELATION_KEYS = [
62- "consent_url" ,
63- "error_url" ,
64- "login_url" ,
65- "oidc_error_url" ,
66- "device_verification_url" ,
67- "post_device_done_url" ,
68- "recovery_url" ,
69- "settings_url" ,
70- "webauthn_settings_url" ,
71- ]
63+
64+ class LoginUIProviderData (BaseModel ):
65+ consent_url : Optional [str ] = None
66+ error_url : Optional [str ] = None
67+ login_url : Optional [str ] = None
68+ oidc_error_url : Optional [str ] = None
69+ device_verification_url : Optional [str ] = None
70+ post_device_done_url : Optional [str ] = None
71+ recovery_url : Optional [str ] = None
72+ registration_url : Optional [str ] = None
73+ settings_url : Optional [str ] = None
74+ webauthn_settings_url : Optional [str ] = None
7275
7376
7477class LoginUIEndpointsRelationReadyEvent (EventBase ):
@@ -100,29 +103,15 @@ def __init__(self, charm: CharmBase, relation_name: str = RELATION_NAME):
100103 def _on_provider_endpoints_relation_created (self , event : RelationCreatedEvent ) -> None :
101104 self .on .ready .emit ()
102105
103- def send_endpoints_relation_data (self , endpoint : str ) -> None :
106+ def send_endpoints_relation_data (self , data : LoginUIProviderData ) -> None :
104107 """Updates relation with endpoint info."""
105108 if not self ._charm .unit .is_leader ():
106- raise LoginUINonLeaderOperationError ()
109+ return None
107110
108111 relations = self .model .relations [self ._relation_name ]
109112
110- if not endpoint :
111- endpoint_databag = {k : "" for k in RELATION_KEYS }
112- else :
113- endpoint_databag = {
114- "consent_url" : f"{ endpoint } /ui/consent" ,
115- "error_url" : f"{ endpoint } /ui/error" ,
116- "login_url" : f"{ endpoint } /ui/login" ,
117- "oidc_error_url" : f"{ endpoint } /ui/oidc_error" ,
118- "device_verification_url" : f"{ endpoint } /ui/device_code" ,
119- "post_device_done_url" : f"{ endpoint } /ui/device_complete" ,
120- "recovery_url" : f"{ endpoint } /ui/reset_email" ,
121- "settings_url" : f"{ endpoint } /ui/reset_password" ,
122- "webauthn_settings_url" : f"{ endpoint } /ui/setup_passkey" ,
123- }
124113 for relation in relations :
125- relation .data [self ._charm .app ].update (endpoint_databag )
114+ relation .data [self ._charm .app ].update (data . model_dump ( exclude_none = True ) )
126115
127116
128117class LoginUIEndpointsRelationError (Exception ):
@@ -131,27 +120,11 @@ class LoginUIEndpointsRelationError(Exception):
131120 pass
132121
133122
134- class LoginUITooManyRelatedAppsError (LoginUIEndpointsRelationError ):
135- """Raised when there are more than one ui-endpoints-info relations between Identity Platform Login UI and another component."""
136-
137- def __init__ (self ) -> None :
138- self .message = f"Too many applications on { RELATION_NAME } "
139- super ().__init__ (self .message )
140-
141-
142- class LoginUINonLeaderOperationError (LoginUIEndpointsRelationError ):
143- """Raised when a non-leader unit wants to call a function intended for leader units."""
144-
145- def __init__ (self ) -> None :
146- self .message = "Calling Identity Platform Login UI unit is not leader"
147- super ().__init__ (self .message )
148-
149-
150- class LoginUIEndpointsRelationMissingError (LoginUIEndpointsRelationError ):
151- """Raised when the relation is missing."""
123+ class LoginUIEndpointsConflictError (LoginUIEndpointsRelationError ):
124+ """Raised when we got the same uri multiple times."""
152125
153126 def __init__ (self ) -> None :
154- self .message = "Missing ui-endpoint-info relation with Identity Platform Login UI "
127+ self .message = "Got the same uri from multiple relations "
155128 super ().__init__ (self .message )
156129
157130
@@ -163,20 +136,35 @@ def __init__(self, charm: CharmBase, relation_name: str = RELATION_NAME):
163136 self .charm = charm
164137 self ._relation_name = relation_name
165138
166- def get_login_ui_endpoints (self , relation_id = None ) -> Dict :
167- """Get the Identity Platform Login UI endpoints."""
139+ @property
140+ def relations (self ) -> List [Relation ]:
141+ """The list of Relation instances associated with this relation_name."""
142+ return [
143+ relation
144+ for relation in self .charm .model .relations [self ._relation_name ]
145+ if relation .active
146+ ]
168147
169- try :
170- ui_endpoint_relation = self .model .get_relation (self ._relation_name , relation_id = relation_id )
171- except TooManyRelatedAppsError :
172- raise LoginUITooManyRelatedAppsError ()
148+ def _get_login_ui_endpoints_data (self , relation : Relation ) -> Optional [Dict ]:
149+ return relation .data [relation .app ] if relation .app else None
173150
174- if ui_endpoint_relation is None :
175- raise LoginUIEndpointsRelationMissingError ()
151+ def get_login_ui_endpoints (self , relation_id = None ) -> Optional [Dict ]:
152+ """Get the Identity Platform Login UI endpoints."""
153+ if relation_id :
154+ relations = [self .model .get_relation (self ._relation_name , relation_id = relation_id )]
155+ else :
156+ relations = self .relations
176157
177- if not ui_endpoint_relation . app :
178- raise LoginUIEndpointsRelationMissingError ()
158+ if not relations :
159+ return None
179160
180- ui_endpoint_relation_data = ui_endpoint_relation .data [ui_endpoint_relation .app ]
161+ data = {}
162+ for r in relations :
163+ d = self ._get_login_ui_endpoints_data (r )
164+ if not d :
165+ continue
166+ if set (d .keys ()).intersection (data .keys ()):
167+ raise LoginUIEndpointsConflictError ()
168+ data .update (d )
181169
182- return ui_endpoint_relation_data
170+ return data
0 commit comments