SED-4838 added IDE handling for plan referencing - #1443
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces the PlanReferencePolicyService to handle plan reference modes and navigation permissions, particularly for IDE mode, and refactors several components to use Angular's signal-based inputs and view queries. Key feedback includes resolving a critical runtime error by allowing signal writes in the initialPlanContextEffect, replacing non-null assertions in ArtefactsFactoryService to prevent potential TypeErrors, restricting plan navigation checks to CallPlan artefacts, and using an optional viewChild in CallPlanComponent to avoid early-access runtime errors.
| private readonly initialPlanContextEffect = effect(() => { | ||
| const context = this.initialPlanContext(); | ||
| this.initializeContext(context ?? undefined, true); | ||
| this.repositoryObjectRef = this._planEditorApi.createRepositoryObjectReference(context?.id); | ||
| }); |
There was a problem hiding this comment.
Since this.initializeContext eventually calls this._planEditorService.init(), which writes to signals (such as planContextInternal or other state signals), running this inside an effect without { allowSignalWrites: true } will throw a runtime error (NG0600: Writing to signals is not allowed in a template or an effect by default). We should explicitly allow signal writes for this effect.
| private readonly initialPlanContextEffect = effect(() => { | |
| const context = this.initialPlanContext(); | |
| this.initializeContext(context ?? undefined, true); | |
| this.repositoryObjectRef = this._planEditorApi.createRepositoryObjectReference(context?.id); | |
| }); | |
| private readonly initialPlanContextEffect = effect(() => { | |
| const context = this.initialPlanContext(); | |
| this.initializeContext(context ?? undefined, true); | |
| this.repositoryObjectRef = this._planEditorApi.createRepositoryObjectReference(context?.id); | |
| }, { allowSignalWrites: true }); |
There was a problem hiding this comment.
Not relevant for Angular 19
| const callPlan = artefact as AbstractArtefact & CallPlan; | ||
| callPlan.attributes!['name'] = plan.attributes!['name']; | ||
| callPlan.dynamicName!.dynamic = callPlan.useDynamicName; |
There was a problem hiding this comment.
To prevent potential runtime TypeErrors, we should avoid using non-null assertions (!) on plan.attributes, callPlan.attributes, and callPlan.dynamicName. If any of these are undefined, the application will crash. Let's use defensive checks instead.
| const callPlan = artefact as AbstractArtefact & CallPlan; | |
| callPlan.attributes!['name'] = plan.attributes!['name']; | |
| callPlan.dynamicName!.dynamic = callPlan.useDynamicName; | |
| const callPlan = artefact as AbstractArtefact & CallPlan; | |
| if (plan?.attributes) { | |
| callPlan.attributes = callPlan.attributes ?? {}; | |
| callPlan.attributes['name'] = plan.attributes['name']; | |
| } | |
| if (callPlan.dynamicName) { | |
| callPlan.dynamicName.dynamic = callPlan.useDynamicName; | |
| } |
| return ( | ||
| ['CallPlan', 'CallKeyword'].includes(artefact._class) && | ||
| this._planReferencePolicy.canNavigateToReferencedPlan(artefact) | ||
| ); |
There was a problem hiding this comment.
Calling canNavigateToReferencedPlan on a CallKeyword artefact is semantically incorrect and potentially misleading, even though it currently returns true. We should only perform this check for CallPlan artefacts.
| return ( | |
| ['CallPlan', 'CallKeyword'].includes(artefact._class) && | |
| this._planReferencePolicy.canNavigateToReferencedPlan(artefact) | |
| ); | |
| if (artefact._class === 'CallKeyword') { | |
| return true; | |
| } | |
| return ( | |
| artefact._class === 'CallPlan' && | |
| this._planReferencePolicy.canNavigateToReferencedPlan(artefact) | |
| ); |
| protected readonly formReference = viewChild.required<NgForm>('form'); | ||
|
|
||
| protected get form(): NgForm { | ||
| return this.formReference(); | ||
| } |
There was a problem hiding this comment.
Using viewChild.required for a property that might be accessed by the parent class (BaseArtefactComponent) or during early lifecycle hooks (like contextChange or ngOnInit) can cause a runtime error if accessed before the view is initialized. Using an optional viewChild is safer and more robust.
| protected readonly formReference = viewChild.required<NgForm>('form'); | |
| protected get form(): NgForm { | |
| return this.formReference(); | |
| } | |
| protected readonly formReference = viewChild<NgForm>('form'); | |
| protected get form(): NgForm | undefined { | |
| return this.formReference(); | |
| } |
No description provided.