55from typing import Any , Dict , List , Optional , Type , Union
66from urllib .parse import urlencode
77
8- from pydantic import ConfigDict , Field , field_validator , model_validator
8+ from pydantic import (
9+ ConfigDict ,
10+ Field ,
11+ field_validator ,
12+ model_serializer ,
13+ model_validator ,
14+ )
915from typing_extensions import Self
1016
1117from _nebari import constants
@@ -109,6 +115,42 @@ class KubeSpawner(schema.Base):
109115 model_config = ConfigDict (extra = "allow" )
110116
111117
118+ class ProfileOptionUnlistedChoice (schema .Base ):
119+ enabled : bool = False
120+ display_name : str
121+ display_name_in_choices : Optional [str ] = None
122+ validation_regex : Optional [str ] = None
123+ validation_message : Optional [str ] = None
124+ kubespawner_override : Dict [str , Any ]
125+
126+
127+ class ProfileOptionChoice (schema .Base ):
128+ display_name : str
129+ default : Optional [bool ] = False
130+ kubespawner_override : Dict [str , Any ]
131+
132+
133+ class ProfileOption (schema .Base ):
134+ display_name : str
135+ unlisted_choice : Optional [ProfileOptionUnlistedChoice ] = None
136+ choices : Dict [str , ProfileOptionChoice ]
137+
138+ @field_validator ("choices" )
139+ def validate_choices (cls , v ):
140+ defaults = [choice for choice in v .values () if choice .default ]
141+ if len (defaults ) > 1 :
142+ raise ValueError ("Only one choice can be marked as default" )
143+ return v
144+
145+ # We need to exclude unlisted_choice if not set.
146+ # This was the recommended solution without affecting the parent.
147+ # reference: https://github.com/pydantic/pydantic/discussions/7315
148+ @model_serializer (mode = "wrap" )
149+ def serialize_model (self , handler ) -> dict [str , Any ]:
150+ result = handler (self )
151+ return {k : v for k , v in result .items () if v is not None }
152+
153+
112154class JupyterLabProfile (schema .Base ):
113155 access : AccessEnum = AccessEnum .all
114156 display_name : str
@@ -117,6 +159,7 @@ class JupyterLabProfile(schema.Base):
117159 users : Optional [List [str ]] = None
118160 groups : Optional [List [str ]] = None
119161 kubespawner_override : Optional [KubeSpawner ] = None
162+ profile_options : Optional [dict [str , ProfileOption ]] = None
120163
121164 @model_validator (mode = "after" )
122165 def only_yaml_can_have_groups_and_users (self ):
0 commit comments