[BI] Resolve listener configuration change crashing BI issue#1541
[BI] Resolve listener configuration change crashing BI issue#1541samithkavishke wants to merge 1 commit intowso2:release/bi-1.8.xfrom
Conversation
📝 WalkthroughWalkthroughThe ServiceConfigureView component now manages position state locally instead of relying solely on props, synchronizing it across service fetch, listener detachment, save operations, and listener changes. A new refreshServicePosition helper was added to recompute position from the project structure based on the current service identifier. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Did you try adding an annotation to http service via the config panel? |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceDesigner/ServiceConfigureView.tsx (1)
217-301:⚠️ Potential issue | 🟠 MajorSync
positionstate with props and use it consistently
positionnow drivesfetchService, but it never updates whenprops.positionchanges, and some consumers still readprops.position(e.g.,ServiceEditView). This can leave edits/saves targeting stale line ranges when the parent navigates to another service or after listener-only updates, which risks the same crash you’re trying to avoid.🔧 Suggested fix
const [position, setPosition] = useState<NodePosition>(props.position); +useEffect(() => { + setPosition(props.position); +}, [props.position]); + useEffect(() => { fetchService(position); }, [position]);Also switch remaining
props.positionusages that should track the refreshed state (e.g.,ServiceEditView position={position}and the fallback position insidesetServiceListeners).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceDesigner/ServiceConfigureView.tsx` around lines 217 - 301, The component's local state position is not kept in sync with props.position, causing fetchService (and consumers like ServiceEditView and setServiceListeners) to operate on stale ranges; update the component to sync state when props.position changes (e.g., useEffect watching props.position to call setPosition), then replace remaining direct uses of props.position with the synchronized position state (notably where fetchService, ServiceEditView position={...}, and the fallback in setServiceListeners are used) so all readers use the current position value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceDesigner/ServiceConfigureView.tsx`:
- Around line 700-723: In refreshServicePosition, guard against entryPoint being
undefined and against a missing optional position before calling setPosition to
avoid breaking fetchService: after locating entryPoint with .find(...) check if
entryPoint exists and if entryPoint.position is defined, otherwise log a clear
error (or return early) and do not call setPosition; update the function around
the entryPoint lookup (symbols: refreshServicePosition, entryPoint, setPosition,
ProjectStructureArtifactResponse.position, fetchService) to handle both cases
safely.
---
Outside diff comments:
In
`@workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceDesigner/ServiceConfigureView.tsx`:
- Around line 217-301: The component's local state position is not kept in sync
with props.position, causing fetchService (and consumers like ServiceEditView
and setServiceListeners) to operate on stale ranges; update the component to
sync state when props.position changes (e.g., useEffect watching props.position
to call setPosition), then replace remaining direct uses of props.position with
the synchronized position state (notably where fetchService, ServiceEditView
position={...}, and the fallback in setServiceListeners are used) so all readers
use the current position value.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceDesigner/ServiceConfigureView.tsx
| const refreshServicePosition = async () => { | ||
| if (!currentIdentifier) { | ||
| console.error("No current identifier available for refreshing service position"); | ||
| return; | ||
| } | ||
|
|
||
| try { | ||
| const projectStructureResponse = await rpcClient.getBIDiagramRpcClient().getProjectStructure(); | ||
| const project = projectStructureResponse.projects.find(p => p.projectPath === props.projectPath); | ||
|
|
||
| if (!project) { | ||
| console.error("Project not found in structure response"); | ||
| return; | ||
| } | ||
|
|
||
| const entryPoint = project | ||
| .directoryMap[DIRECTORY_MAP.SERVICE] | ||
| .find((service: ProjectStructureArtifactResponse) => service.name === currentIdentifier); | ||
|
|
||
| setPosition(entryPoint.position); | ||
| } catch (error) { | ||
| console.error('Error refreshing service position:', error); | ||
| } | ||
| }; |
There was a problem hiding this comment.
Guard against missing service entry/position in refreshServicePosition
ProjectStructureArtifactResponse.position is optional, and .find(...) can return undefined. As written, setPosition(entryPoint.position) can throw or set undefined, which will break fetchService.
🛡️ Suggested fix
- const entryPoint = project
- .directoryMap[DIRECTORY_MAP.SERVICE]
- .find((service: ProjectStructureArtifactResponse) => service.name === currentIdentifier);
-
- setPosition(entryPoint.position);
+ const services = project.directoryMap?.[DIRECTORY_MAP.SERVICE] ?? [];
+ const entryPoint = services.find(
+ (service: ProjectStructureArtifactResponse) => service.name === currentIdentifier
+ );
+
+ if (!entryPoint?.position) {
+ console.error("Service position not found for", currentIdentifier);
+ return;
+ }
+ setPosition(entryPoint.position);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const refreshServicePosition = async () => { | |
| if (!currentIdentifier) { | |
| console.error("No current identifier available for refreshing service position"); | |
| return; | |
| } | |
| try { | |
| const projectStructureResponse = await rpcClient.getBIDiagramRpcClient().getProjectStructure(); | |
| const project = projectStructureResponse.projects.find(p => p.projectPath === props.projectPath); | |
| if (!project) { | |
| console.error("Project not found in structure response"); | |
| return; | |
| } | |
| const entryPoint = project | |
| .directoryMap[DIRECTORY_MAP.SERVICE] | |
| .find((service: ProjectStructureArtifactResponse) => service.name === currentIdentifier); | |
| setPosition(entryPoint.position); | |
| } catch (error) { | |
| console.error('Error refreshing service position:', error); | |
| } | |
| }; | |
| const refreshServicePosition = async () => { | |
| if (!currentIdentifier) { | |
| console.error("No current identifier available for refreshing service position"); | |
| return; | |
| } | |
| try { | |
| const projectStructureResponse = await rpcClient.getBIDiagramRpcClient().getProjectStructure(); | |
| const project = projectStructureResponse.projects.find(p => p.projectPath === props.projectPath); | |
| if (!project) { | |
| console.error("Project not found in structure response"); | |
| return; | |
| } | |
| const services = project.directoryMap?.[DIRECTORY_MAP.SERVICE] ?? []; | |
| const entryPoint = services.find( | |
| (service: ProjectStructureArtifactResponse) => service.name === currentIdentifier | |
| ); | |
| if (!entryPoint?.position) { | |
| console.error("Service position not found for", currentIdentifier); | |
| return; | |
| } | |
| setPosition(entryPoint.position); | |
| } catch (error) { | |
| console.error('Error refreshing service position:', error); | |
| } | |
| }; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@workspaces/ballerina/ballerina-visualizer/src/views/BI/ServiceDesigner/ServiceConfigureView.tsx`
around lines 700 - 723, In refreshServicePosition, guard against entryPoint
being undefined and against a missing optional position before calling
setPosition to avoid breaking fetchService: after locating entryPoint with
.find(...) check if entryPoint exists and if entryPoint.position is defined,
otherwise log a clear error (or return early) and do not call setPosition;
update the function around the entryPoint lookup (symbols:
refreshServicePosition, entryPoint, setPosition,
ProjectStructureArtifactResponse.position, fetchService) to handle both cases
safely.
There are multiple related issues in the http service configuration which will be addressed here wso2/product-ballerina-integrator#2568 |
Purpose
Fixes: wso2/product-ballerina-integrator#2533
Goals
Approach
Refetch the listener position and data if only the listener changes.
UI Component Development
npm run storybookfrom the root directory to view current components.Manage Icons
User stories
BICrashingWhenListenerChange.mov
Release note
Documentation
Training
Certification
Marketing
Automation tests
Security checks
Samples
Related PRs
Migrations (if applicable)
Test environment
Learning
Summary by CodeRabbit