Developer Guide
Code Architecture
The O2 GUIs follow a layered architecture:
  - Controllers: Handle incoming requests, validate input, and coordinate responses. Controllers should not contain business logic.
 
  - High-level Services: Contain business logic and orchestrate calls to lower-level services or services dealing with external systems.
 
  - Low-level Services: Encapsulate direct interactions with external systems (e.g., gRPC clients, databases, or file systems).
 
  - Utilities: Shared helper functions and classes used across services and controllers.
 
Typical flow:
Controller → High-level Service → Low-level Service → External System
Coding Conventions
  - Controller Method Naming:
Controller methods should end with handler.
Example: getEnvironmentDetailsHandler, deployEnvironmentHandler 
  - Service Method Naming:
Service methods that are fetching data from an external service should start with retrieve.
Example: retrieveWorkflowMappings, retrieveHostsToIgnore 
  - Linting: Code must pass ESLint checks - see 
package.json for command (e.g. npm run eslint) 
  - Error Handling:
    
      - Services should throw errors; controllers are responsible for catching and responding.
 
      - Use custom error classes (e.g., 
InvalidInputError) for clarity. 
    
   
  - Logging: Use the provided O2 logger (
this._logger) for warnings and errors. 
  - Testing:
    
      - Write unit, integration, front-end, API tests (as per your use case) for all new features and bug fixes.
 
      - Use Mocha and Sinon for testing and mocking.
 
      - Place tests in the 
test/ directory, mirroring the source structure. 
    
   
Adding a New Feature
  - Create/Update Controller:
    
      - Add a new method or endpoint in the relevant controller, ending the method name with 
handler. 
      - Validate input and call the appropriate service method.
 
    
   
  - Implement Service Logic:
    
      - Add or update a method in a high-level service.
 
      - If the method fetches data from an external service, start its name with 
retrieve. 
      - Encapsulate business logic and coordinate calls to low-level services.
 
    
   
  - Update/Implement Low-level Service:
    
      - Add methods for direct communication with external systems if needed.
 
    
   
  - Write/Update Tests:
    
      - Add unit tests for new or changed logic.
 
      - Ensure tests cover edge cases and error handling.
 
    
   
  - Document:
    
      - Update this README or add comments/docstrings as needed.
 
    
   
Best Practices
  - Keep controllers thin: Only handle request/response logic.
 
  - Keep services focused: Each service should have a clear responsibility.
 
  - Write meaningful commit messages.
 
  - Use async/await for asynchronous code.
 
  - Prefer immutability: Avoid mutating input objects.
 
  - Handle all possible error cases and provide clear error messages.
 
  - Review and update documentation with every change.