11#![ allow( unused_imports, unused_variables) ]
22use std:: {
3- collections:: HashMap , error:: Error as StdError , sync:: RwLock , thread:: JoinHandle ,
3+ collections:: HashMap ,
4+ error:: Error as StdError ,
5+ sync:: { Arc , RwLock } ,
6+ thread:: JoinHandle ,
47 time:: Duration ,
58} ;
69
@@ -9,7 +12,7 @@ use actix_web::{
912 App , Error , HttpRequest , HttpResponse , HttpServer , Responder ,
1013 dev:: ServerHandle ,
1114 http:: header:: { self } ,
12- middleware,
15+ middleware, post ,
1316 web:: { self , Data , route} ,
1417} ;
1518use convert_case:: { Case , Casing } ;
@@ -19,6 +22,7 @@ use juniper_graphql_ws::ConnectionConfig;
1922use log:: { debug, error, info, trace, warn} ;
2023#[ cfg( feature = "explorer" ) ]
2124use rust_embed:: RustEmbed ;
25+ use serde:: { Deserialize , Serialize } ;
2226use surfpool_core:: scenarios:: TemplateRegistry ;
2327use surfpool_gql:: {
2428 DynamicSchema ,
@@ -29,8 +33,8 @@ use surfpool_gql::{
2933} ;
3034use surfpool_studio_ui:: serve_studio_static_files;
3135use surfpool_types:: {
32- DataIndexingCommand , OverrideTemplate , SanitizedConfig , SubgraphCommand , SubgraphEvent ,
33- SurfpoolConfig ,
36+ DataIndexingCommand , OverrideTemplate , SanitizedConfig , Scenario , SubgraphCommand ,
37+ SubgraphEvent , SurfpoolConfig ,
3438} ;
3539use txtx_core:: kit:: types:: types:: Value ;
3640use txtx_gql:: kit:: uuid:: Uuid ;
@@ -68,6 +72,7 @@ pub async fn start_subgraph_and_explorer_server(
6872
6973 // Initialize template registry and load templates
7074 let template_registry_wrapped = Data :: new ( RwLock :: new ( TemplateRegistry :: new ( ) ) ) ;
75+ let loaded_scenarios = Data :: new ( RwLock :: new ( LoadedScenarios :: new ( ) ) ) ;
7176
7277 let subgraph_handle = start_subgraph_runloop (
7378 subgraph_events_tx,
@@ -86,12 +91,12 @@ pub async fn start_subgraph_and_explorer_server(
8691 . app_data ( config_wrapped. clone ( ) )
8792 . app_data ( collections_metadata_lookup_wrapped. clone ( ) )
8893 . app_data ( template_registry_wrapped. clone ( ) )
94+ . app_data ( loaded_scenarios. clone ( ) )
8995 . wrap (
9096 Cors :: default ( )
9197 . allow_any_origin ( )
92- . allowed_methods ( vec ! [ "POST" , "GET" , "OPTIONS" , "DELETE" ] )
93- . allowed_headers ( vec ! [ header:: AUTHORIZATION , header:: ACCEPT ] )
94- . allowed_header ( header:: CONTENT_TYPE )
98+ . allow_any_method ( )
99+ . allow_any_header ( )
95100 . supports_credentials ( )
96101 . max_age ( 3600 ) ,
97102 )
@@ -100,6 +105,10 @@ pub async fn start_subgraph_and_explorer_server(
100105 . service ( get_config)
101106 . service ( get_indexers)
102107 . service ( get_scenario_templates)
108+ . service ( post_scenarios)
109+ . service ( get_scenarios)
110+ . service ( delete_scenario)
111+ . service ( patch_scenario)
103112 . service (
104113 web:: scope ( "/workspace" )
105114 . route ( "/v1/indexers" , web:: post ( ) . to ( post_graphql) )
@@ -109,6 +118,7 @@ pub async fn start_subgraph_and_explorer_server(
109118 ) ;
110119
111120 if enable_studio {
121+ app = app. app_data ( Arc :: new ( RwLock :: new ( LoadedScenarios :: new ( ) ) ) ) ;
112122 app = app. service ( serve_studio_static_files) ;
113123 }
114124
@@ -191,6 +201,106 @@ async fn get_scenario_templates(
191201 . body ( response) )
192202}
193203
204+ #[ derive( Debug , Serialize , Deserialize ) ]
205+ pub struct LoadedScenarios {
206+ pub scenarios : Vec < Scenario > ,
207+ }
208+ impl LoadedScenarios {
209+ pub fn new ( ) -> Self {
210+ Self {
211+ scenarios : Vec :: new ( ) ,
212+ }
213+ }
214+ }
215+
216+ #[ post( "/v1/scenarios" ) ]
217+ async fn post_scenarios (
218+ req : HttpRequest ,
219+ scenario : web:: Json < Scenario > ,
220+ data : Data < RwLock < LoadedScenarios > > ,
221+ ) -> Result < HttpResponse , Error > {
222+ let mut loaded_scenarios = data
223+ . write ( )
224+ . map_err ( |_| actix_web:: error:: ErrorInternalServerError ( "Failed to acquire write lock" ) ) ?;
225+ let scenario_data = scenario. into_inner ( ) ;
226+ let scenario_id = scenario_data. id . clone ( ) ;
227+ loaded_scenarios. scenarios . push ( scenario_data) ;
228+ let response = serde_json:: json!( { "id" : scenario_id} ) ;
229+ Ok ( HttpResponse :: Ok ( )
230+ . content_type ( "application/json" )
231+ . body ( response. to_string ( ) ) )
232+ }
233+
234+ #[ actix_web:: get( "/v1/scenarios" ) ]
235+ async fn get_scenarios ( data : Data < RwLock < LoadedScenarios > > ) -> Result < HttpResponse , Error > {
236+ let loaded_scenarios = data
237+ . read ( )
238+ . map_err ( |_| actix_web:: error:: ErrorInternalServerError ( "Failed to acquire read lock" ) ) ?;
239+ let response = serde_json:: to_string ( & loaded_scenarios. scenarios ) . map_err ( |_| {
240+ actix_web:: error:: ErrorInternalServerError ( "Failed to serialize loaded scenarios" )
241+ } ) ?;
242+
243+ Ok ( HttpResponse :: Ok ( )
244+ . content_type ( "application/json" )
245+ . body ( response) )
246+ }
247+
248+ #[ actix_web:: delete( "/v1/scenarios/{id}" ) ]
249+ async fn delete_scenario (
250+ path : web:: Path < String > ,
251+ data : Data < RwLock < LoadedScenarios > > ,
252+ ) -> Result < HttpResponse , Error > {
253+ let scenario_id = path. into_inner ( ) ;
254+ let mut loaded_scenarios = data
255+ . write ( )
256+ . map_err ( |_| actix_web:: error:: ErrorInternalServerError ( "Failed to acquire write lock" ) ) ?;
257+
258+ let initial_len = loaded_scenarios. scenarios . len ( ) ;
259+ loaded_scenarios. scenarios . retain ( |s| s. id != scenario_id) ;
260+
261+ if loaded_scenarios. scenarios . len ( ) == initial_len {
262+ return Ok (
263+ HttpResponse :: NotFound ( ) . body ( format ! ( "Scenario with id '{}' not found" , scenario_id) )
264+ ) ;
265+ }
266+
267+ Ok ( HttpResponse :: Ok ( ) . body ( format ! ( "Scenario '{}' deleted" , scenario_id) ) )
268+ }
269+
270+ #[ actix_web:: patch( "/v1/scenarios/{id}" ) ]
271+ async fn patch_scenario (
272+ path : web:: Path < String > ,
273+ scenario : web:: Json < Scenario > ,
274+ data : Data < RwLock < LoadedScenarios > > ,
275+ ) -> Result < HttpResponse , Error > {
276+ let scenario_id = path. into_inner ( ) ;
277+ let mut loaded_scenarios = data
278+ . write ( )
279+ . map_err ( |_| actix_web:: error:: ErrorInternalServerError ( "Failed to acquire write lock" ) ) ?;
280+
281+ let scenario_index = loaded_scenarios
282+ . scenarios
283+ . iter ( )
284+ . position ( |s| s. id == scenario_id) ;
285+
286+ match scenario_index {
287+ Some ( index) => {
288+ loaded_scenarios. scenarios [ index] = scenario. into_inner ( ) ;
289+ let response = serde_json:: json!( { "id" : scenario_id} ) ;
290+ Ok ( HttpResponse :: Ok ( )
291+ . content_type ( "application/json" )
292+ . body ( response. to_string ( ) ) )
293+ }
294+ None => {
295+ loaded_scenarios. scenarios . push ( scenario. into_inner ( ) ) ;
296+ let response = serde_json:: json!( { "id" : scenario_id} ) ;
297+ Ok ( HttpResponse :: Ok ( )
298+ . content_type ( "application/json" )
299+ . body ( response. to_string ( ) ) )
300+ }
301+ }
302+ }
303+
194304#[ allow( dead_code) ]
195305#[ cfg( not( feature = "explorer" ) ) ]
196306fn handle_embedded_file ( _path : & str ) -> HttpResponse {
0 commit comments