1616using Semver ;
1717using ServerlessWorkflow . Sdk . Models ;
1818using Synapse . Api . Client . Services ;
19- using Synapse . Dashboard . Components . DocumentDetailsStateManagement ;
20- using Synapse . Dashboard . Components . ResourceEditorStateManagement ;
2119using Synapse . Resources ;
20+ using System . Text . RegularExpressions ;
2221
2322namespace Synapse . Dashboard . Pages . Workflows . Create ;
2423
@@ -50,7 +49,8 @@ MonacoInterop monacoInterop
5049
5150 private TextModel ? _textModel = null ;
5251 private string _textModelUri = string . Empty ;
53- private bool _disposed ;
52+ private bool _disposed = false ;
53+ private bool _processingVersion = false ;
5454
5555 /// <summary>
5656 /// Gets the service used to perform logging
@@ -122,6 +122,10 @@ MonacoInterop monacoInterop
122122 /// Gets an <see cref="IObservable{T}"/> used to observe changes to the state's <see cref="CreateWorkflowViewState.WorkflowDefinition"/> property
123123 /// </summary>
124124 public IObservable < WorkflowDefinition ? > WorkflowDefinition => this . Select ( state => state . WorkflowDefinition ) . DistinctUntilChanged ( ) ;
125+ /// <summary>
126+ /// Gets an <see cref="IObservable{T}"/> used to observe changes to the state's <see cref="CreateWorkflowViewState.WorkflowDefinitionText"/> property
127+ /// </summary>
128+ public IObservable < string ? > WorkflowDefinitionText => this . Select ( state => state . WorkflowDefinitionText ) . DistinctUntilChanged ( ) ;
125129
126130 /// <summary>
127131 /// Gets an <see cref="IObservable{T}"/> used to observe changes to the state's <see cref="CreateWorkflowViewState.Loading"/> property
@@ -296,16 +300,17 @@ public async Task OnTextBasedEditorInitAsync()
296300 /// <returns></returns>
297301 public async Task SetTextBasedEditorLanguageAsync ( )
298302 {
303+ if ( this . TextEditor == null )
304+ {
305+ return ;
306+ }
299307 try
300308 {
301309 var language = this . MonacoEditorHelper . PreferredLanguage ;
302- if ( this . TextEditor != null )
303- {
304- this . _textModel = await Global . GetModel ( this . JSRuntime , this . _textModelUri ) ;
305- this . _textModel ??= await Global . CreateModel ( this . JSRuntime , "" , language , this . _textModelUri ) ;
306- await Global . SetModelLanguage ( this . JSRuntime , this . _textModel , language ) ;
307- await this . TextEditor ! . SetModel ( this . _textModel ) ;
308- }
310+ this . _textModel = await Global . GetModel ( this . JSRuntime , this . _textModelUri ) ;
311+ this . _textModel ??= await Global . CreateModel ( this . JSRuntime , "" , language , this . _textModelUri ) ;
312+ await Global . SetModelLanguage ( this . JSRuntime , this . _textModel , language ) ;
313+ await this . TextEditor ! . SetModel ( this . _textModel ) ;
309314 }
310315 catch ( Exception ex )
311316 {
@@ -320,19 +325,33 @@ public async Task SetTextBasedEditorLanguageAsync()
320325 async Task SetTextEditorValueAsync ( )
321326 {
322327 var document = this . Get ( state => state . WorkflowDefinitionText ) ;
323- if ( this . TextEditor != null && ! string . IsNullOrWhiteSpace ( document ) )
328+ if ( this . TextEditor == null || string . IsNullOrWhiteSpace ( document ) )
324329 {
325- await this . TextEditor . SetValue ( document ) ;
326- try
327- {
328- //await this.TextEditor.Trigger("", "editor.action.triggerSuggest");
329- await this . TextEditor . Trigger ( "" , "editor.action.formatDocument" ) ;
330- }
331- catch ( Exception ex )
332- {
333- this . Logger . LogError ( "Unable to set text editor value: {exception}" , ex . ToString ( ) ) ;
334- }
330+ return ;
335331 }
332+ await this . TextEditor . SetValue ( document ) ;
333+ try
334+ {
335+ await this . TextEditor . Trigger ( "" , "editor.action.formatDocument" ) ;
336+ }
337+ catch ( Exception ex )
338+ {
339+ this . Logger . LogError ( "Unable to set text editor value: {exception}" , ex . ToString ( ) ) ;
340+ }
341+ }
342+
343+ /// <summary>
344+ /// Handles text editor content changes
345+ /// </summary>
346+ /// <param name="e">The <see cref="ModelContentChangedEvent"/></param>
347+ /// <returns>An awaitable task</returns>
348+ public async Task OnDidChangeModelContent ( ModelContentChangedEvent e )
349+ {
350+ if ( this . TextEditor == null ) return ;
351+ var document = await this . TextEditor . GetValue ( ) ;
352+ this . Reduce ( state => state with {
353+ WorkflowDefinitionText = document
354+ } ) ;
336355 }
337356
338357 /// <summary>
@@ -446,10 +465,6 @@ public override async Task InitializeAsync()
446465 string document = "" ;
447466 if ( definition != null )
448467 {
449- if ( definition . Document ? . Dsl != null )
450- {
451- await this . SetValidationSchema ( $ "v{ definition . Document . Dsl } ") ;
452- }
453468 document = this . MonacoEditorHelper . PreferredLanguage == PreferredLanguage . JSON ?
454469 this . JsonSerializer . SerializeToText ( definition ) :
455470 this . YamlSerializer . SerializeToText ( definition ) ;
@@ -468,6 +483,25 @@ public override async Task InitializeAsync()
468483 {
469484 await this . GetWorkflowDefinitionAsync ( workflow . ns , workflow . name ) ;
470485 } , cancellationToken : this . CancellationTokenSource . Token ) ;
486+ this . WorkflowDefinitionText . Where ( document => ! string . IsNullOrEmpty ( document ) ) . Throttle ( new ( 100 ) ) . SubscribeAsync ( async ( document ) => {
487+ if ( string . IsNullOrWhiteSpace ( document ) )
488+ {
489+ return ;
490+ }
491+ var currentDslVersion = this . Get ( state => state . DslVersion ) ;
492+ var versionExtractor = new Regex ( "'?\" ?(dsl|DSL)'?\" ?\\ s*:\\ s*'?\" ?([\\ w\\ .\\ -\\ +]*)'?\" ?" ) ;
493+ var match = versionExtractor . Match ( document ) ;
494+ if ( match == null )
495+ {
496+ return ;
497+ }
498+ var documentDslVersion = match . Groups [ 2 ] . Value ;
499+ if ( documentDslVersion == currentDslVersion )
500+ {
501+ return ;
502+ }
503+ await this . SetValidationSchema ( "v" + documentDslVersion ) ;
504+ } , cancellationToken : this . CancellationTokenSource . Token ) ;
471505 await base . InitializeAsync ( ) ;
472506 }
473507
@@ -479,10 +513,34 @@ public override async Task InitializeAsync()
479513 protected async Task SetValidationSchema ( string ? version = null )
480514 {
481515 version ??= await this . SpecificationSchemaManager . GetLatestVersion ( ) ;
482- var schema = await this . SpecificationSchemaManager . GetSchema ( version ) ;
483- var type = $ "create_{ typeof ( WorkflowDefinition ) . Name . ToLower ( ) } _{ version } ";
484- await this . MonacoInterop . AddValidationSchemaAsync ( schema , $ "https://synapse.io/schemas/{ type } .json", $ "{ type } *") . ConfigureAwait ( false ) ;
485- this . _textModelUri = this . MonacoEditorHelper . GetResourceUri ( type ) ;
516+ var currentVersion = this . Get ( state => state . DslVersion ) ;
517+ if ( currentVersion == version )
518+ {
519+ return ;
520+ }
521+ if ( this . _processingVersion )
522+ {
523+ return ;
524+ }
525+ this . SetProblemDetails ( null ) ;
526+ this . _processingVersion = true ;
527+ try
528+ {
529+ var schema = await this . SpecificationSchemaManager . GetSchema ( version ) ;
530+ var type = $ "create_{ typeof ( WorkflowDefinition ) . Name . ToLower ( ) } _{ version } _schema";
531+ await this . MonacoInterop . AddValidationSchemaAsync ( schema , $ "https://synapse.io/schemas/{ type } .json", $ "{ type } *") . ConfigureAwait ( false ) ;
532+ this . _textModelUri = this . MonacoEditorHelper . GetResourceUri ( type ) ;
533+ this . Reduce ( state => state with
534+ {
535+ DslVersion = version
536+ } ) ;
537+ }
538+ catch ( Exception ex )
539+ {
540+ this . Logger . LogError ( "Unable to set the validation schema: {exception}" , ex . ToString ( ) ) ;
541+ this . SetProblemDetails ( new ProblemDetails ( new Uri ( "about:blank" ) , "Unable to set the validation schema" , 404 , $ "Unable to set the validation schema for the specification version '{ version } '. Make sure the version exists.") ) ;
542+ }
543+ this . _processingVersion = false ;
486544 }
487545
488546 /// <summary>
0 commit comments