@@ -36,6 +36,10 @@ defmodule SentinelCp.Services.Service do
3636 field :traffic_split , :map , default: % { }
3737 field :service_type , :string , default: "standard"
3838 field :inference , :map , default: % { }
39+ field :grpc , :map , default: % { }
40+ field :websocket , :map , default: % { }
41+ field :graphql , :map , default: % { }
42+ field :streaming , :map , default: % { }
3943 field :redirect_url , :string
4044
4145 belongs_to :project , SentinelCp.Projects.Project
@@ -78,6 +82,10 @@ defmodule SentinelCp.Services.Service do
7882 :traffic_split ,
7983 :service_type ,
8084 :inference ,
85+ :grpc ,
86+ :websocket ,
87+ :graphql ,
88+ :streaming ,
8189 :redirect_url ,
8290 :upstream_group_id ,
8391 :certificate_id ,
@@ -90,8 +98,8 @@ defmodule SentinelCp.Services.Service do
9098 |> validate_length ( :name , min: 1 , max: 100 )
9199 |> validate_route_path ( )
92100 |> validate_route_type ( )
93- |> validate_inclusion ( :service_type , ~w( standard inference) )
94- |> validate_inference_config ( )
101+ |> validate_inclusion ( :service_type , ~w( standard inference grpc websocket graphql streaming ) )
102+ |> validate_service_type_config ( )
95103 |> generate_slug ( )
96104 |> validate_slug ( )
97105 |> unique_constraint ( [ :project_id , :slug ] , error_key: :slug )
@@ -125,6 +133,10 @@ defmodule SentinelCp.Services.Service do
125133 :traffic_split ,
126134 :service_type ,
127135 :inference ,
136+ :grpc ,
137+ :websocket ,
138+ :graphql ,
139+ :streaming ,
128140 :redirect_url ,
129141 :upstream_group_id ,
130142 :certificate_id ,
@@ -136,36 +148,68 @@ defmodule SentinelCp.Services.Service do
136148 |> validate_length ( :name , min: 1 , max: 100 )
137149 |> validate_route_path ( )
138150 |> validate_route_type ( )
139- |> validate_inclusion ( :service_type , ~w( standard inference) )
140- |> validate_inference_config ( )
151+ |> validate_inclusion ( :service_type , ~w( standard inference grpc websocket graphql streaming ) )
152+ |> validate_service_type_config ( )
141153 end
142154
143- defp validate_inference_config ( changeset ) do
155+ @ type_config_fields % {
156+ "inference" => :inference ,
157+ "grpc" => :grpc ,
158+ "websocket" => :websocket ,
159+ "graphql" => :graphql ,
160+ "streaming" => :streaming
161+ }
162+
163+ defp validate_service_type_config ( changeset ) do
144164 service_type = get_field ( changeset , :service_type )
145- inference = get_field ( changeset , :inference )
146165
147- case service_type do
148- "inference" ->
149- cond do
150- not is_map ( inference ) or inference == % { } ->
151- add_error ( changeset , :inference , "is required when service_type is inference" )
166+ # Validate the active type's config is present
167+ changeset = validate_active_type_config ( changeset , service_type )
152168
153- Map . get ( inference , "provider" ) not in ~w( openai anthropic generic) ->
154- add_error ( changeset , :inference , "must include a valid provider (openai, anthropic, generic)" )
169+ # Validate all inactive type configs are empty
170+ Enum . reduce ( @ type_config_fields , changeset , fn { type , field } , cs ->
171+ if type != service_type do
172+ value = get_field ( cs , field )
155173
156- true ->
157- changeset
158- end
159-
160- _ ->
161- if is_map ( inference ) and inference != % { } do
162- add_error ( changeset , :inference , "must be empty for standard services" )
174+ if is_map ( value ) and value != % { } do
175+ add_error ( cs , field , "must be empty when service_type is not #{ type } " )
163176 else
164- changeset
177+ cs
165178 end
179+ else
180+ cs
181+ end
182+ end )
183+ end
184+
185+ defp validate_active_type_config ( changeset , "inference" ) do
186+ inference = get_field ( changeset , :inference )
187+
188+ cond do
189+ not is_map ( inference ) or inference == % { } ->
190+ add_error ( changeset , :inference , "is required when service_type is inference" )
191+
192+ Map . get ( inference , "provider" ) not in ~w( openai anthropic generic) ->
193+ add_error ( changeset , :inference , "must include a valid provider (openai, anthropic, generic)" )
194+
195+ true ->
196+ changeset
166197 end
167198 end
168199
200+ defp validate_active_type_config ( changeset , type ) when type in ~w( grpc websocket graphql streaming) do
201+ field = Map . fetch! ( @ type_config_fields , type )
202+ value = get_field ( changeset , field )
203+
204+ if not is_map ( value ) or value == % { } do
205+ add_error ( changeset , field , "is required when service_type is #{ type } " )
206+ else
207+ changeset
208+ end
209+ end
210+
211+ defp validate_active_type_config ( changeset , _standard ) , do: changeset
212+
169213 defp validate_route_path ( changeset ) do
170214 validate_format ( changeset , :route_path , ~r/ ^\/ / , message: "must start with /" )
171215 end
0 commit comments