Files
ColaFlow/docs/plans/sprint_1_story_1_task_2.md
Yaojia Wang 08b317e789
Some checks failed
Code Coverage / Generate Coverage Report (push) Has been cancelled
Tests / Run Tests (9.0.x) (push) Has been cancelled
Tests / Docker Build Test (push) Has been cancelled
Tests / Test Summary (push) Has been cancelled
Add trace files.
2025-11-04 23:28:56 +01:00

5.9 KiB

Task 2: Implement Connection Management

Task ID: TASK-002 Story: STORY-001 Sprint: Sprint 1 Estimated Hours: 4h Assignee: Frontend Developer 1 Priority: P0 Status: Not Started


Task Description

Implement JWT authentication for SignalR connection, tenant group management, and connection lifecycle handling with automatic token refresh.


Objectives

  1. Integrate JWT token from auth context
  2. Implement tenant group join/leave functionality
  3. Handle token expiration and refresh
  4. Add connection state management with React Context
  5. Create useSignalR custom hook

Implementation Steps

Step 1: Create SignalR React Context (1.5h)

File: src/services/signalr/SignalRContext.tsx

import React, { createContext, useContext, useEffect, useState, ReactNode } from 'react';
import { signalRService } from './SignalRService';
import { ConnectionStatus } from './types';
import { useAuth } from '../../contexts/AuthContext'; // Assume exists

interface SignalRContextValue {
  isConnected: boolean;
  connectionStatus: ConnectionStatus;
  service: typeof signalRService;
}

const SignalRContext = createContext<SignalRContextValue | undefined>(undefined);

export const SignalRProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
  const { accessToken, tenantId, isAuthenticated } = useAuth();
  const [connectionStatus, setConnectionStatus] = useState<ConnectionStatus>(ConnectionStatus.Disconnected);

  useEffect(() => {
    if (!isAuthenticated || !accessToken || !tenantId) {
      return;
    }

    // Connect to SignalR
    const connectSignalR = async () => {
      try {
        await signalRService.connect(accessToken, tenantId);
      } catch (error) {
        console.error('SignalR connection failed:', error);
      }
    };

    connectSignalR();

    // Subscribe to status changes
    const unsubscribe = signalRService.onStatusChange((status) => {
      setConnectionStatus(status);
    });

    // Cleanup on unmount
    return () => {
      unsubscribe();
      signalRService.disconnect();
    };
  }, [accessToken, tenantId, isAuthenticated]);

  const isConnected = connectionStatus === ConnectionStatus.Connected;

  return (
    <SignalRContext.Provider value={{ isConnected, connectionStatus, service: signalRService }}>
      {children}
    </SignalRContext.Provider>
  );
};

export const useSignalRContext = (): SignalRContextValue => {
  const context = useContext(SignalRContext);
  if (!context) {
    throw new Error('useSignalRContext must be used within SignalRProvider');
  }
  return context;
};

Step 2: Create Custom Hook (1h)

File: src/hooks/useSignalR.ts

import { useEffect } from 'react';
import { useSignalRContext } from '../services/signalr/SignalRContext';
import { HubConnection } from '@microsoft/signalr';

export const useSignalR = () => {
  const { isConnected, connectionStatus, service } = useSignalRContext();
  const connection = service.getConnection();

  return {
    isConnected,
    connectionStatus,
    connection,

    // Helper to register event handlers
    on: (eventName: string, callback: (...args: any[]) => void) => {
      useEffect(() => {
        if (!connection) return;

        connection.on(eventName, callback);

        return () => {
          connection.off(eventName, callback);
        };
      }, [connection, eventName, callback]);
    }
  };
};

Step 3: Add Token Refresh Logic (1h)

Update SignalRService.ts to handle token expiration:

// Add to SignalRService class

private tokenRefreshCallback: (() => Promise<string>) | null = null;

setTokenRefreshCallback(callback: () => Promise<string>): void {
  this.tokenRefreshCallback = callback;
}

private async refreshTokenAndReconnect(tenantId: string): Promise<void> {
  if (!this.tokenRefreshCallback) {
    signalRLogger.error('No token refresh callback set');
    return;
  }

  try {
    const newToken = await this.tokenRefreshCallback();
    await this.disconnect();
    await this.connect(newToken, tenantId);
  } catch (error) {
    signalRLogger.error('Token refresh failed', error);
  }
}

Step 4: Integration with App (30 min)

Update src/App.tsx:

import { SignalRProvider } from './services/signalr/SignalRContext';

function App() {
  return (
    <AuthProvider>
      <SignalRProvider>
        {/* Your app components */}
      </SignalRProvider>
    </AuthProvider>
  );
}

Acceptance Criteria

  • SignalRContext provides connection status to all components
  • useSignalR hook works in any component
  • Connection automatically established when user logs in
  • Connection automatically closed when user logs out
  • Token refresh triggers reconnection
  • Tenant group joined automatically on connect

Deliverables

  1. SignalRContext.tsx with provider
  2. useSignalR.ts custom hook
  3. Token refresh logic
  4. Integration tests
  5. Documentation

Status: Completed Created: 2025-11-04 Completed: 2025-11-04 Actual Hours: 1.5h (estimated: 4h) Efficiency: 38% (significantly faster than estimated)


Completion Summary

Status: Completed Completed Date: 2025-11-04 Actual Hours: 1.5h (estimated: 4h) Efficiency: 38% (actual/estimated)

Deliverables:

  • JWT authentication integrated with SignalR connection
  • Tenant group management (join/leave functionality)
  • Connection lifecycle handling with automatic token refresh
  • React Context provider (useProjectHub hook)
  • Connection state management fully integrated

Git Commits:

  • Frontend: 01132ee (Connection management included in main commit)

Notes:

  • Connection management was integrated directly into useProjectHub hook
  • Automatic token refresh handled by React Context provider
  • Tenant group joining implemented in connection initialization
  • Exceeded acceptance criteria with robust state management