Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

automations #1343

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open

automations #1343

wants to merge 14 commits into from

Conversation

cpvalente
Copy link
Owner

@cpvalente cpvalente commented Nov 23, 2024

This is an attempt at re-writing the integrations service

For simplicity, I am renaming this to automation, this means that integrations becomes an internal name used for data into ontime

There are also some dependency upgrades which make the PR larger

The code parsing logic is the same as before, so hopefully there are not that many core changes to the outputs, however, the filtering logic is completely new

I am also proposing a new structure to our backend, it is annoying that I only did it in automations and - if we decide to go forwards with this - I will create a follow up PR to apply everywhere

  • in the same directory, I have collocated all the router and services. the service file, only has business logic. All crud logic is moved out of the way to a dao file
  • in the same way, all utilities like parsing are in that directory. this means the automation directory, contains all logic related to that domain concept
  • i also considered merging the controller and router files, but didnt see an obvious improvement. in a way, it is nice to be able to read all the available routes in a small file without worrying about what they do

In this PR

  • expose an integration service which allows filtering logic before sending a message
  • expose implementations for OSC, HTTP and Companion
  • expose testing function
  • create supporting UI
  • implement triggering of the new service
  • implement template parsing
  • implement clients
  • create migration code for old subscriptions (decided to migrate OSC data and leave the rest)
  • persist data

TODO

  • OSC input port and toggle migrate to settings
  • consider UI for writing templates
  • update docs
  • double-check strict typing in new code
  • double-check deepsource

Copy link
Contributor

coderabbitai bot commented Nov 23, 2024

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This pull request introduces a comprehensive refactoring of the application's integration and automation capabilities. The changes replace the existing OSC and HTTP integration services with a new, more flexible automation system. The new architecture allows for creating and managing automation blueprints and rules that can trigger actions based on timer lifecycle events. The modifications span both client and server-side code, introducing new components for managing automation settings, creating blueprints, and handling output dispatching.

Changes

File/Folder Change Summary
apps/client/src/common/api/ Added automation.ts for API interactions, removed http.ts and osc.ts
apps/client/src/features/app-settings/ Replaced integrations panel with automation panel, added new components for managing automations and blueprints
apps/server/src/api-data/ Introduced new automation-related controllers, routers, services, and validation
apps/server/src/services/integration-service/ Removed OSC and HTTP integration classes and services
apps/server/src/models/ Updated data models to replace OSC and HTTP settings with automation configuration

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@cpvalente cpvalente force-pushed the integrations branch 3 times, most recently from e6ee477 to cd34149 Compare November 25, 2024 07:04
@cpvalente cpvalente changed the title Integrations wip: integrations Nov 29, 2024
@cpvalente cpvalente force-pushed the integrations branch 3 times, most recently from a20ed53 to 1264147 Compare December 29, 2024 11:22
@cpvalente cpvalente force-pushed the integrations branch 14 times, most recently from 6409c38 to 717a1ce Compare January 6, 2025 18:55
@cpvalente cpvalente force-pushed the integrations branch 6 times, most recently from 399fe0e to e3246f5 Compare January 11, 2025 15:55
@cpvalente cpvalente changed the title wip: integrations automations Jan 11, 2025

export const automationPlaceholderSettings: AutomationSettings = {
enabledAutomations: false,
enabledOscIn: false,
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alex-Arc i am wondering if the OSC settings should be in the app settings instead of here

What do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes
at least it should be separate from the automations

const fieldValue = state[field];

// TODO: if value is empty string, the user could be meaning to check if the value does not exist
switch (operator) {
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alex-Arc i have been wondering if we could come up with a few examples and fine tune the operators?

* Expose possibility to send a message using OSC protocol
*/
export function emitOSC(output: OSCOutput, state: RuntimeState) {
const message = preparePayload(output, state);
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skip sending in cloud

@cpvalente cpvalente force-pushed the integrations branch 2 times, most recently from 8f6dbec to 55bd741 Compare January 12, 2025 19:52
const handleTestOSCOutput = async (index: number) => {
try {
const values = getValues(`outputs.${index}`) as OSCOutput;
if (!values.targetIP || !values.targetPort || !values.address || !values.args) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This if statement currently prevents testing OSC output if no args are specified.

@cpvalente
Copy link
Owner Author

@coderabbitai full review

Copy link
Contributor

coderabbitai bot commented Jan 18, 2025

✅ Actions performed

Full review triggered.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 29

🔭 Outside diff range comments (1)
apps/server/src/stores/EventStore.ts (1)

Line range hint 1-44: Consider adding error handling for WebSocket failures.

The store broadcasts state changes via WebSocket but doesn't handle potential connection failures or errors.

Add error handling:

 set<T extends keyof RuntimeStore>(key: T, value: RuntimeStore[T]) {
   store[key] = value;
-  socket.sendAsJson({
-    type: `ontime-${key}`,
-    payload: value,
-  });
+  try {
+    socket.sendAsJson({
+      type: `ontime-${key}`,
+      payload: value,
+    });
+  } catch (error) {
+    console.error('Failed to broadcast store update:', error);
+    // Consider implementing retry logic or error reporting
+  }
 }
🧹 Nitpick comments (54)
apps/server/src/classes/data-provider/DataProvider.utils.ts (1)

Line range hint 3-5: Update JSDoc to reflect automation capabilities.

The current documentation is too generic and doesn't mention the automation aspect of the merge operation.

Consider updating the JSDoc to:

 /**
- * Merges a partial ontime project into a given ontime project
+ * Merges a partial ontime project into a given ontime project, handling automation settings
+ * and other project configurations while preserving existing data where not overridden.
  */
apps/server/src/api-data/automation/clients/http.client.ts (2)

8-14: Enhance JSDoc documentation for better clarity.

The JSDoc could be more descriptive by including:

 /**
  * Expose possibility to send a message using HTTP protocol
+ * @param output - The HTTP output configuration containing the URL template
+ * @param state - The current runtime state used for template parsing
+ * @example
+ * emitHTTP({ url: 'http://example.com/${timer}' }, { timer: '10:00' })
  */

16-20: Add URL validation before returning.

The function should validate the parsed URL to ensure it's well-formed before returning.

 function preparePayload(output: HTTPOutput, state: RuntimeState): string {
   const parsedUrl = parseTemplateNested(output.url, state);
+  try {
+    new URL(parsedUrl); // Validate URL format
+  } catch (error) {
+    logger.warning(LogOrigin.Rx, `Invalid URL format: ${parsedUrl}`);
+    throw new Error(`Invalid URL format: ${parsedUrl}`);
+  }
   return parsedUrl;
 }
apps/server/src/classes/data-provider/__tests__/DataProvider.utils.test.ts (1)

126-131: Consider extracting the default automation configuration into a shared test fixture.

The automation configuration is duplicated between test cases. Consider extracting it into a shared fixture or helper function to maintain DRY principles and make future updates easier.

+ const DEFAULT_AUTOMATION_CONFIG = {
+   enabledAutomations: false,
+   enabledOscIn: false,
+   oscPortIn: 8000,
+   automations: [],
+   blueprints: {},
+ };

  const existingData = {
    rundown: [],
    // ... other properties
-   automation: {
-     enabledAutomations: false,
-     enabledOscIn: false,
-     oscPortIn: 8000,
-     automations: [],
-     blueprints: {},
-   },
+   automation: DEFAULT_AUTOMATION_CONFIG,
  } as DatabaseModel;
apps/server/src/utils/parserFunctions.ts (1)

Line range hint 251-252: Track the technical debt for removing lowercase key compatibility.

The TODO comment indicates a temporary backwards compatibility measure that can be removed in October 2024. Consider creating a tracking issue to ensure this technical debt is addressed at the appropriate time.

Would you like me to create a GitHub issue to track the removal of lowercase key compatibility in October 2024?

apps/client/src/features/app-settings/panel-utils/PanelUtils.module.scss (1)

194-208: LGTM! Well-structured modifiers with semantic naming.

The renaming from .inlineSiblings to .inlineElements and the addition of modifiers improves clarity and flexibility. The BEM-like structure makes the code more maintainable.

Consider extracting the gap values into SCSS variables for better maintainability:

+$gap-small: 0.5rem;
+$gap-medium: 1rem;

 .inlineElements {
   display: flex;
   align-items: center;

   &.inner {
-    gap: 0.5rem;
+    gap: $gap-small;
   }
   &.component {
-    gap: 1rem;
+    gap: $gap-medium;
   }
apps/server/src/api-data/automation/automation.router.ts (5)

13-13: Consider removing the .js extension from import paths.

TypeScript typically doesn't require .js extensions in import paths as they are handled by the TypeScript compiler.

-} from './automation.controller.js';
+} from './automation.controller';

26-27: Consider adding error handling middleware.

While the routes are well-structured, they might benefit from error handling middleware to ensure consistent error responses.

Example implementation:

const errorHandler = (err: Error, req: Request, res: Response, next: NextFunction) => {
  console.error(err.stack);
  res.status(500).json({ error: 'Internal Server Error' });
};

router.use(errorHandler);

Also, consider adding validation for the GET endpoint to ensure query parameters are valid if any are expected.


33-35: Consider adding GET endpoints and reviewing route naming consistency.

  1. Missing GET endpoints for:

    • List all blueprints (GET /blueprint)
    • Get a specific blueprint (GET /blueprint/:id)
  2. Consider using plural form for resource names to follow REST conventions:

-router.post('/blueprint', validateBlueprint, postBlueprint);
-router.put('/blueprint/:id', validateBlueprintPatch, editBlueprint);
-router.delete('/blueprint/:id', paramContainsId, deleteBlueprint);
+router.post('/blueprints', validateBlueprint, postBlueprint);
+router.put('/blueprints/:id', validateBlueprintPatch, editBlueprint);
+router.delete('/blueprints/:id', paramContainsId, deleteBlueprint);

37-37: Consider using a more specific route path for the test endpoint.

The current /test path is quite generic. Consider making it more specific to better reflect its purpose:

-router.post('/test', validateTestPayload, testOutput);
+router.post('/test-output', validateTestPayload, testOutput);

1-37: Well-structured router implementation with clear separation of concerns.

The router implementation successfully achieves the PR objectives by:

  • Separating validation logic from route handlers
  • Providing a clean API structure for automation and blueprints
  • Supporting the new filtering logic through appropriate endpoints

The architecture is solid and maintainable.

Consider documenting the API endpoints using OpenAPI/Swagger for better API documentation and client generation.

apps/server/src/app.ts (3)

Line range hint 92-95: Update the OntimeStartOrder enum to reflect current initialization steps.

Since startIntegrations and its related initialization step InitIO are no longer needed, consider removing InitIO from the OntimeStartOrder enum to keep it consistent with the current initialization process.

Apply this diff to update the enum:

 enum OntimeStartOrder {
   Error,
   InitAssets,
   InitServer,
-  InitIO,
 }

Line range hint 188-190: Consider removing the now-unused startIntegrations function.

The startIntegrations function is currently empty, indicating that it may no longer be necessary after the refactoring. Removing unused functions helps keep the codebase clean and maintainable.

Apply this diff to remove the unused function:

-export const startIntegrations = async () => {
-  checkStart(OntimeStartOrder.InitIO);
-};

Line range hint 100-109: Revise the checkStart function for consistency with updated start order.

With the removal of InitIO, ensure that the checkStart function's logic is updated accordingly. Specifically, check the conditions and transitions related to OntimeStartOrder to prevent any potential errors due to the change in initialization steps.

apps/server/src/api-data/automation/automation.service.ts (2)

76-99: Ensure type safety when comparing fieldValue and value.

Currently, the code uses loose equality == and != to compare fieldValue and value, allowing for type coercion (e.g., '10' == 10). This can lead to unexpected behavior and bugs due to implicit type conversions.

Consider using strict equality === and !== for more predictable comparisons, or explicitly convert types before comparison to ensure the values are of the same type.

Apply this diff to use strict equality:

         switch (operator) {
           case 'equals':
-            return fieldValue == value;
+            return fieldValue === value;
           case 'not_equals':
-            return fieldValue != value;
+            return fieldValue !== value;

93-96: Extend handling of non-string fieldValue in 'contains' and 'not_contains' operators.

The 'contains' and 'not_contains' operators currently check if fieldValue is a string before using includes(value). If fieldValue is not a string, the function returns false.

Consider supporting additional types, such as arrays, to make the condition evaluation more robust. For example, if fieldValue could be an array, you might want to check if it includes the value.

Apply this diff to handle arrays:

         case 'contains':
-          return typeof fieldValue === 'string' && fieldValue.includes(value);
+          if (typeof fieldValue === 'string') {
+            return fieldValue.includes(value);
+          }
+          if (Array.isArray(fieldValue)) {
+            return fieldValue.includes(value);
+          }
+          return false;
         case 'not_contains':
-          return typeof fieldValue === 'string' && !fieldValue.includes(value);
+          if (typeof fieldValue === 'string') {
+            return !fieldValue.includes(value);
+          }
+          if (Array.isArray(fieldValue)) {
+            return !fieldValue.includes(value);
+          }
+          return false;
apps/client/src/features/app-settings/panel/automations-panel/BlueprintForm.tsx (4)

285-410: Refactor to reduce code duplication in output forms rendering.

The code blocks for rendering OSC and HTTP outputs are similar, with variations tailored to their specific fields. Consider abstracting common functionality into reusable components or custom hooks to reduce duplication, improve maintainability, and enhance readability.


97-113: Provide user feedback on errors during OSC output testing.

Currently, errors during OSC output testing are silently ignored, which may lead to user confusion if the test fails. Consider handling errors by displaying a notification or an error message to inform the user of any issues encountered.


115-129: Provide user feedback on errors during HTTP output testing.

Errors during HTTP output testing are not communicated to the user. Improving error handling by providing user feedback will enhance the user experience and help in troubleshooting configuration issues.


245-265: Remove redundant isDisabled and isLoading properties set to false.

Properties like isDisabled={false} and isLoading={false} are unnecessary because their default values are false. Removing them can simplify the code and improve readability.

Apply this diff to remove redundant properties:

- isDisabled={false}
- isLoading={false}

Also applies to: 350-365, 396-410

apps/server/src/models/dataModel.ts (1)

34-40: Add type definitions and validation for automation settings.

While the structure looks good, consider these improvements:

  1. Add TypeScript interfaces for automations and blueprints.
  2. Add validation for oscPortIn (typical range: 1024-65535).
  3. Document the purpose of these default values.

Example type definitions:

interface AutomationBlueprint {
  // Add blueprint properties
}

interface Automation {
  // Add automation properties
}

interface AutomationSettings {
  enabledAutomations: boolean;
  enabledOscIn: boolean;
  oscPortIn: number;
  automations: Automation[];
  blueprints: Record<string, AutomationBlueprint>;
}
apps/server/src/stores/__mocks__/runtimeState.mocks.ts (1)

44-45: Consider safer type handling for merged state.

The type assertion as RuntimeState might hide potential type mismatches. Consider:

  1. Using a type guard to validate the merged state.
  2. Or using a typed version of deepmerge.

Example with type guard:

function isRuntimeState(obj: unknown): obj is RuntimeState {
  // Add runtime state validation
  return true; // implement validation logic
}

export function makeRuntimeStateData(patch?: Partial<RuntimeState>): RuntimeState {
  const merged = deepmerge(baseState, patch);
  if (!isRuntimeState(merged)) {
    throw new Error('Invalid runtime state after merge');
  }
  return merged;
}
apps/client/src/features/app-settings/panel/automations-panel/__tests__/automationUtils.test.ts (1)

7-11: Consider adding edge cases to the test suite.

The current test suite could benefit from additional edge cases:

  • Empty array input
  • Single entry array
  • Case sensitivity in titles
  • Multiple sets of duplicates
apps/client/src/theme/ontimeRadio.ts (1)

3-9: Consider extracting color constants.

The color codes are currently hard-coded with comments indicating their semantic names. Consider extracting these into theme constants for better maintainability and consistency.

+// In theme/colors.ts
+export const colors = {
+  gray1200: '#262626',
+  uiWhite: '#f6f6f6',
+  // ... other colors
+};

+// In ontimeRadio.ts
 control: {
-  borderColor: '#262626', // $gray-1200
+  borderColor: colors.gray1200,
   // ... apply similar changes to other color values
 }
apps/server/src/utils/assert.ts (1)

31-40: Consider enhancing the hasKeys function.

The implementation is solid but could be improved with:

  1. Type safety for nested keys
  2. Optional keys support
  3. Value type validation

Example enhancement:

export function hasKeys<
  T extends object,
  K extends keyof any,
  Required extends K[] = K[],
  Optional extends K[] = K[]
>(
  value: T,
  required: Required,
  optional: Optional = [] as Optional,
): asserts value is T & Record<Required[number], unknown> & Partial<Record<Optional[number], unknown>> {
  for (const key of required) {
    if (!(key in value)) {
      throw new Error(`Required key not found: ${String(key)}`);
    }
  }
}
apps/client/src/common/hooks-query/useAutomationSettings.ts (1)

15-16: Consider adjusting the retry delay strategy.

The exponential backoff (attempt * 2500ms) might lead to very long delays for later retry attempts. For example, the 5th retry would wait 12.5 seconds. Consider using a more moderate strategy or capping the maximum delay.

-    retry: 5,
-    retryDelay: (attempt: number) => attempt * 2500,
+    retry: 3,
+    retryDelay: (attempt: number) => Math.min(attempt * 1000, 3000),
apps/client/src/features/app-settings/panel/automations-panel/automationUtils.ts (1)

34-51: Optimize duplicate checking logic.

The current implementation can be simplified using a Set for better performance and readability.

 export function checkDuplicates(automations: Automation[]) {
-  const automationMap: Record<string, string[]> = {};
+  const seen = new Set<string>();
   const duplicates = [];
 
   for (let i = 0; i < automations.length; i++) {
     const automation = automations[i];
-    if (!Object.hasOwn(automationMap, automation.trigger)) {
-      automationMap[automation.trigger] = [];
-    }
+    const key = `${automation.trigger}-${automation.blueprintId}`;
 
-    if (automationMap[automation.trigger].includes(automation.blueprintId)) {
+    if (seen.has(key)) {
       duplicates.push(i);
     } else {
-      automationMap[automation.trigger].push(automation.blueprintId);
+      seen.add(key);
     }
   }
   return duplicates.length > 0 ? duplicates : undefined;
 }
apps/client/src/common/api/automation.ts (2)

36-39: Add response type validation for mutation endpoints.

The mutation endpoints should validate the response data. Consider adding a validation layer:

function validateAutomation(data: unknown): Automation {
  if (!isAutomation(data)) {
    throw new Error('Invalid automation data received from server');
  }
  return data;
}

export async function addAutomation(automation: AutomationDTO): Promise<Automation> {
  const res = await axios.post<unknown>(`${automationsPath}/automation`, automation);
  return validateAutomation(res.data);
}

Also applies to: 44-47, 59-62, 67-70


83-85: Add timeout for test output endpoint.

The test endpoint should have a reasonable timeout to prevent hanging:

export function testOutput(output: AutomationOutput): Promise<void> {
  return axios.post(`${automationsPath}/test`, output, {
    timeout: 5000, // 5 seconds
  });
}
apps/client/src/features/app-settings/panel/automations-panel/AutomationsListItem.tsx (2)

13-22: Add JSDoc documentation for the interface.

Consider adding JSDoc documentation to describe the purpose and usage of each prop in the AutomationsListItemProps interface. This will improve code maintainability and help other developers understand how to use this component.

+/**
+ * Props for the AutomationsListItem component
+ * @property {NormalisedAutomationBlueprint} blueprints - Collection of available automation blueprints
+ * @property {string} id - Unique identifier for the automation
+ * @property {string} title - Display title for the automation
+ * @property {TimerLifeCycle} trigger - Event that triggers the automation
+ * @property {string} blueprintId - ID of the associated blueprint
+ * @property {boolean} [duplicate] - Flag indicating if this is a duplicate automation
+ * @property {() => void} handleDelete - Callback for deletion
+ * @property {() => void} postSubmit - Callback after successful form submission
+ */
 interface AutomationsListItemProps {
   blueprints: NormalisedAutomationBlueprint;
   id: string;

31-31: Replace arbitrary colSpan value.

The colSpan={99} is an arbitrary value that might not work correctly with all table layouts. Consider using a more semantic value or calculating it based on the actual number of columns.

-        <td colSpan={99}>
+        <td colSpan={4}> {/* Based on the actual number of columns in the table */}
apps/client/src/features/app-settings/panel/automations-panel/AutomationsList.tsx (4)

15-18: Consider enhancing type safety for the props interface.

The interface could benefit from making the props more specific:

 interface AutomationsListProps {
-  automations: Automation[];
+  automations: readonly Automation[];
-  blueprints: NormalisedAutomationBlueprint;
+  blueprints: Readonly<NormalisedAutomationBlueprint>;
 }

26-34: Clear error state before new deletion attempts.

The error state should be cleared when starting a new deletion to prevent stale errors from being displayed.

 const handleDelete = async (id: string) => {
+  setDeleteError(null);
   try {
     await deleteAutomation(id);
   } catch (error) {
     setDeleteError(maybeAxiosError(error));
   } finally {
     refetch();
   }
 };

65-69: Fix grammatical error in duplicate warning message.

The error message for duplicates has a grammatical error.

 {duplicates && (
   <Panel.Error>
-    You have created multiple links between the same trigger and blueprint which can performance issues.
+    You have created multiple links between the same trigger and blueprint which can cause performance issues.
   </Panel.Error>
 )}

102-108: Simplify delete error display logic.

The delete error display can be simplified by moving it outside the mapping function since it's not specific to any automation.

- {deleteError && (
-   <tr>
-     <td colSpan={5}>
-       <Panel.Error>{deleteError}</Panel.Error>
-     </td>
-   </tr>
- )}

And add it after the mapping:

+ {deleteError && (
+   <tr>
+     <td colSpan={5}>
+       <Panel.Error>{deleteError}</Panel.Error>
+     </td>
+   </tr>
+ )}
apps/client/src/features/app-settings/panel-utils/PanelUtils.tsx (2)

Line range hint 65-74: Consider enhancing type safety for TableEmpty props.

The props could benefit from more specific types and documentation.

-export function TableEmpty({ label, handleClick }: { label?: string; handleClick?: () => void }) {
+interface TableEmptyProps {
+  /** Custom label to display. Defaults to 'No data yet' */
+  label?: string;
+  /** Optional click handler. Button is disabled when not provided */
+  handleClick?: () => void;
+}
+
+export function TableEmpty({ label, handleClick }: TableEmptyProps) {

127-144: Enhance type constraints and add JSDoc documentation.

The InlineElements component could benefit from better type constraints and documentation.

+/**
+ * Renders inline elements with customizable HTML tag and alignment.
+ * @template C - The HTML tag to use for rendering
+ */
 type AllowedInlineTags = 'div' | 'td';
 type InlineProps<C extends AllowedInlineTags> = {
+  /** Optional HTML tag to use. Defaults to 'div' */
   as?: C;
+  /** Defines the relation type for styling. Defaults to 'component' */
   relation?: 'inner' | 'component' | 'section';
+  /** Alignment of the inline elements. Defaults to 'start' */
   align?: 'start' | 'end';
   className?: string;
-};
+} & Omit<JSX.IntrinsicElements[C], keyof InlineProps<C>>;
apps/client/src/features/app-settings/panel/automations-panel/BlueprintsList.tsx (2)

26-64: Consider improving error handling and state updates.

Two suggestions for enhancing the implementation:

  1. Consider implementing optimistic updates for delete operations to improve perceived performance
  2. Make error states blueprint-specific instead of component-wide to avoid confusion when multiple operations fail
-const [deleteError, setDeleteError] = useState<string | null>(null);
+const [deleteErrors, setDeleteErrors] = useState<Record<string, string>>({});

 const handleDelete = async (id: string) => {
   try {
-    setDeleteError(null);
+    setDeleteErrors(prev => ({ ...prev, [id]: null }));
     await deleteBlueprint(id);
+    // Optimistic update
+    const newBlueprints = { ...blueprints };
+    delete newBlueprints[id];
+    // Update local state immediately
+    setBlueprintFormData(null);
   } catch (error) {
-    setDeleteError(maybeAxiosError(error));
+    setDeleteErrors(prev => ({ ...prev, [id]: maybeAxiosError(error) }));
   }
 };

66-126: Enhance accessibility and theming.

Consider the following improvements:

  1. Add ARIA labels and roles to the table for better screen reader support
  2. Move hard-coded colors to the theme system
-<Panel.Table>
+<Panel.Table role="grid" aria-label="Automation blueprints">
   <thead>
-    <tr>
+    <tr role="row">
-      <th style={{ width: '45%' }}>Title</th>
+      <th role="columnheader" style={{ width: '45%' }}>Title</th>

-      color='#e2e2e2' // $gray-200
+      color='gray.200'
-      color='#FA5656' // $red-500
+      color='red.500'
apps/client/src/features/app-settings/panel/automations-panel/AutomationSettingsForm.tsx (2)

38-45: Enhance error handling specificity.

The current error handling sets a root-level error message. Consider handling specific error cases differently.

 const onSubmit = async (formData: AutomationSettingsProps) => {
   try {
     await editAutomationSettings(formData);
   } catch (error) {
     const message = maybeAxiosError(error);
-    setError('root', { message });
+    if (message.includes('port')) {
+      setError('oscPortIn', { message });
+    } else if (message.includes('automation')) {
+      setError('enabledAutomations', { message });
+    } else {
+      setError('root', { message });
+    }
   }
 };

146-154: LGTM! Comprehensive port validation.

The validation rules for the OSC port are well-implemented with proper range checks.

Consider adding a more user-friendly message for the numeric pattern validation:

 pattern: {
   value: isOnlyNumbers,
-  message: 'Value should be numeric',
+  message: 'Port number must contain only digits',
 },
apps/server/src/api-data/automation/automation.validation.ts (2)

27-42: Extract common validation patterns.

Consider creating reusable validation chains to reduce code duplication.

+const commonValidations = {
+  title: body('title').exists().isString().trim(),
+  trigger: body('trigger').exists().isIn(timerLifecycleValues),
+  blueprintId: body('blueprintId').exists().isString().trim(),
+};

 export const validateAutomationSettings = [
   body('enabledAutomations').exists().isBoolean(),
   body('enabledOscIn').exists().isBoolean(),
   body('oscPortIn').exists().isPort(),
   body('automations').optional().isArray(),
-  body('automations.*.title').optional().isString().trim(),
-  body('automations.*.trigger').optional().isIn(timerLifecycleValues),
-  body('automations.*.blueprintId').optional().isString().trim(),
+  body('automations.*').optional().custom((value) => {
+    commonValidations.title.run(value);
+    commonValidations.trigger.run(value);
+    commonValidations.blueprintId.run(value);
+    return true;
+  }),
   body('blueprints').optional().custom(parseBluePrint),

132-167: Enhance error messages for output validation.

The current error messages could be more descriptive to help users identify and fix issues.

-      throw new Error('Invalid automation');
+      throw new Error(`Invalid output type: ${type}. Supported types are 'osc' and 'http'`);

 function validateOSCOutput(payload: object): payload is OSCOutput {
-  assert.hasKeys(payload, ['targetIP', 'targetPort', 'address', 'args']);
+  const requiredKeys = ['targetIP', 'targetPort', 'address', 'args'];
+  assert.hasKeys(payload, requiredKeys, `OSC output missing required fields: ${requiredKeys.join(', ')}`);

   if (typeof args !== 'string' && typeof args !== 'number') {
-    throw new Error('Invalid automation');
+    throw new Error('OSC args must be either a string or a number');
   }
apps/server/src/api-data/automation/__tests__/automation.dao.test.ts (1)

42-232: Consider adding more edge cases to the test suite.

The test coverage is good, but consider adding these scenarios:

  1. Test validation of malformed blueprint data
  2. Test concurrent modifications
  3. Test maximum length constraints

Example additional test:

it('should reject malformed blueprint data', () => {
  const invalidData = {
    title: '',  // Empty title
    filterRule: 'invalid_rule',
    filters: null,  // Invalid filters
    outputs: [{ type: 'unknown' }],  // Invalid output type
  };
  
  expect(() => addBlueprint(invalidData)).toThrow();
});
apps/server/src/api-data/automation/__tests__/automation.utils.test.ts (1)

169-241: Add edge case tests for stringToOSCArgs.

While the current test coverage is good, consider adding tests for the following edge cases:

  • Invalid number formats
  • Mixed case boolean values (e.g., "True", "FALSE")
  • Malformed quoted strings (unclosed quotes)
apps/client/src/features/app-settings/panel/automations-panel/BlueprintForm.module.scss (2)

27-50: Consider extracting common styles to reduce duplication.

Multiple sections share identical styles for labels. Consider extracting these common styles into a shared class to improve maintainability.

+.formLabel {
+  font-size: calc(1rem - 3px);
+  color: $label-gray;
+}

 .titleSection,
 .ruleSection,
 .filterSection,
 .oscSection,
 .httpSection,
 .companionSection {
   display: grid;
   grid-gap: 0.5rem;

   button {
     align-self: end;
   }
-  
-  label {
-    font-size: calc(1rem - 3px);
-    color: $label-gray;
-  }
+  
+  label {
+    @extend .formLabel;
+  }
 }

57-67: Consider responsive design for grid layouts.

The fixed grid column sizes might not work well on smaller screens. Consider adding media queries to adjust the grid layout for different viewport sizes.

 .filterSection {
   grid-template-columns: 2fr 1fr 2fr auto;
+  
+  @media (max-width: 768px) {
+    grid-template-columns: 1fr;
+    gap: 1rem;
+  }
 }

 .oscSection {
   grid-template-columns: 9rem 5rem 3fr 4fr auto;
+  
+  @media (max-width: 768px) {
+    grid-template-columns: 1fr;
+    gap: 1rem;
+  }
 }
apps/client/src/common/utils/__tests__/regex.test.ts (1)

29-30: Enhance test coverage for URL validation.

While adding HTTPS support is good, consider adding more edge cases to improve test coverage:

  • URLs with different paths and query parameters
  • URLs with ports
  • URLs with special characters
-    const right = ['https://test', 'http://test'];
-    const wrong = ['testing', '123.0.1'];
+    const right = [
+      'https://test',
+      'http://test',
+      'https://test:8080',
+      'http://test/path?query=1',
+      'https://sub.domain.com'
+    ];
+    const wrong = [
+      'testing',
+      '123.0.1',
+      'ftp://test',
+      'httptest',
+      'http:/test'
+    ];
apps/client/src/features/app-settings/panel/automations-panel/AutomationForm.tsx (3)

31-36: Consider enhancing type safety with explicit form values type.

Instead of using AutomationDTO directly, consider creating a specific form values type that matches your form fields exactly. This will help catch potential mismatches between form fields and DTO properties early.

- } = useForm<AutomationDTO>({
+ type AutomationFormValues = Pick<AutomationDTO, 'title' | 'trigger' | 'blueprintId'>;
+ } = useForm<AutomationFormValues>({

44-63: Enhance error handling and type safety in form submission.

The error handling could be improved by:

  1. Adding specific error types for different failure scenarios
  2. Providing more detailed error messages to users
  3. Handling network timeouts
   const onSubmit = async (values: AutomationDTO) => {
+    const timeout = 5000; // 5 seconds
     if (initialId) {
       try {
-        await editAutomation(initialId, { id: initialId, ...values });
+        await Promise.race([
+          editAutomation(initialId, { id: initialId, ...values }),
+          new Promise((_, reject) => 
+            setTimeout(() => reject(new Error('Request timed out')), timeout)
+          ),
+        ]);
         postSubmit();
       } catch (error) {
+        const errorMessage = error instanceof Error && error.message === 'Request timed out'
+          ? 'Failed to save changes: Request timed out'
+          : `Failed to save changes to automation: ${maybeAxiosError(error)}`;
-        setError('root', { message: `Failed to save changes to automation ${maybeAxiosError(error)}` });
+        setError('root', { message: errorMessage });
       }

77-119: Enhance form accessibility.

Consider adding the following accessibility improvements:

  1. ARIA labels for form controls
  2. Error message association with form fields
  3. Better focus management
       <label>
         Title
         <Input
+          aria-label="Automation title"
+          aria-describedby="title-error"
           {...register('title', { required: { value: true, message: 'Required field' } })}
           size='sm'
           variant='ontime-filled'
           autoComplete='off'
           defaultValue={initialTitle}
         />
-        <Panel.Error>{errors.title?.message}</Panel.Error>
+        <Panel.Error id="title-error">{errors.title?.message}</Panel.Error>
       </label>
apps/server/src/api-data/automation/automation.utils.ts (3)

8-14: Strengthen type validation functions.

The validation functions could be more robust by:

  1. Using type guards more effectively
  2. Adding input validation
  3. Making the functions more maintainable
+const FILTER_OPERATORS = ['equals', 'not_equals', 'greater_than', 'less_than', 'contains'] as const;
+const FILTER_RULES = ['all', 'any'] as const;
+
+type FilterOperator = typeof FILTER_OPERATORS[number];
+type FilterRule = typeof FILTER_RULES[number];
+
 export function isFilterOperator(value: string): value is FilterOperator {
-  return ['equals', 'not_equals', 'greater_than', 'less_than', 'contains'].includes(value);
+  return FILTER_OPERATORS.includes(value as FilterOperator);
 }

 export function isFilterRule(value: string): value is FilterRule {
-  return value === 'all' || value === 'any';
+  return FILTER_RULES.includes(value as FilterRule);
 }

Line range hint 55-88: Optimize template processing and improve maintainability.

Consider the following improvements to the template processing logic:

  1. Cache the regex pattern
  2. Add error boundaries for invalid templates
  3. Improve type safety
-const placeholderRegex = /{{(.*?)}}/g;
+// Cache the compiled regex pattern
+const PLACEHOLDER_REGEX = /{{(.*?)}}/g;
+const MAX_REPLACEMENTS = 100; // Prevent infinite loops
+
 export function parseTemplateNested(template: string, state: object, humanReadable = quickAliases): string {
+  if (typeof template !== 'string') {
+    throw new Error('Template must be a string');
+  }
+
   let parsedTemplate = template;
-  const matches = Array.from(parsedTemplate.matchAll(placeholderRegex));
+  let replacementCount = 0;
+  const matches = Array.from(parsedTemplate.matchAll(PLACEHOLDER_REGEX));

   for (const match of matches) {
+    if (replacementCount++ > MAX_REPLACEMENTS) {
+      throw new Error('Maximum template replacements exceeded');
+    }

90-109: Enhance helper function documentation and type safety.

The formatDisplayFromString function could be improved with:

  1. Better JSDoc documentation
  2. Stronger type safety
  3. Input validation
 /**
  * Handles the specific case where the MaybeNumber is encoded in a string
  * After parsed, the value is formatted to a human-readable string
+ * @param value - The string value to format
+ * @param hideZero - Whether to hide leading zeros in the output
+ * @returns A formatted string representation of the value
+ * @throws {Error} If the input value is invalid
  */
 function formatDisplayFromString(value: string, hideZero = false): string {
+  if (typeof value !== 'string') {
+    throw new Error('Value must be a string');
+  }
+
   let valueInNumber: MaybeNumber = null;

   // the value will be a string, so we need to convert it to the Maybe type
   if (value !== 'null') {
     const parsedValue = Number(value);
     if (!Number.isNaN(parsedValue)) {
       valueInNumber = parsedValue;
     }
   }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aa929fd and 0985d84.

⛔ Files ignored due to path filters (10)
  • apps/server/test-db/db.json is excluded by none and included by none
  • e2e/tests/fixtures/test-db.json is excluded by none and included by none
  • packages/types/src/api/ontime-controller/BackendResponse.type.ts is excluded by none and included by none
  • packages/types/src/definitions/DataModel.type.ts is excluded by none and included by none
  • packages/types/src/definitions/core/Automation.type.ts is excluded by none and included by none
  • packages/types/src/definitions/core/HttpSettings.type.ts is excluded by none and included by none
  • packages/types/src/definitions/core/OscSettings.type.ts is excluded by none and included by none
  • packages/types/src/definitions/core/TimerLifecycle.type.ts is excluded by none and included by none
  • packages/types/src/index.ts is excluded by none and included by none
  • packages/types/src/utils/guards.ts is excluded by none and included by none
📒 Files selected for processing (77)
  • apps/client/src/common/api/automation.ts (1 hunks)
  • apps/client/src/common/api/constants.ts (1 hunks)
  • apps/client/src/common/api/http.ts (0 hunks)
  • apps/client/src/common/api/osc.ts (0 hunks)
  • apps/client/src/common/hooks-query/useAutomationSettings.ts (1 hunks)
  • apps/client/src/common/hooks-query/useHttpSettings.ts (0 hunks)
  • apps/client/src/common/hooks-query/useOscSettings.ts (0 hunks)
  • apps/client/src/common/models/AutomationSettings.ts (1 hunks)
  • apps/client/src/common/models/Http.ts (0 hunks)
  • apps/client/src/common/models/Info.ts (0 hunks)
  • apps/client/src/common/models/OscSettings.ts (0 hunks)
  • apps/client/src/common/utils/__tests__/regex.test.ts (1 hunks)
  • apps/client/src/common/utils/regex.ts (1 hunks)
  • apps/client/src/features/app-settings/AppSettings.tsx (2 hunks)
  • apps/client/src/features/app-settings/panel-content/PanelContent.module.scss (1 hunks)
  • apps/client/src/features/app-settings/panel-utils/PanelUtils.module.scss (3 hunks)
  • apps/client/src/features/app-settings/panel-utils/PanelUtils.tsx (3 hunks)
  • apps/client/src/features/app-settings/panel/automations-panel/AutomationForm.tsx (1 hunks)
  • apps/client/src/features/app-settings/panel/automations-panel/AutomationPanel.tsx (1 hunks)
  • apps/client/src/features/app-settings/panel/automations-panel/AutomationSettingsForm.tsx (1 hunks)
  • apps/client/src/features/app-settings/panel/automations-panel/AutomationsList.tsx (1 hunks)
  • apps/client/src/features/app-settings/panel/automations-panel/AutomationsListItem.tsx (1 hunks)
  • apps/client/src/features/app-settings/panel/automations-panel/BlueprintForm.module.scss (1 hunks)
  • apps/client/src/features/app-settings/panel/automations-panel/BlueprintForm.tsx (1 hunks)
  • apps/client/src/features/app-settings/panel/automations-panel/BlueprintsList.tsx (1 hunks)
  • apps/client/src/features/app-settings/panel/automations-panel/__tests__/automationUtils.test.ts (1 hunks)
  • apps/client/src/features/app-settings/panel/automations-panel/automationUtils.ts (1 hunks)
  • apps/client/src/features/app-settings/panel/integrations-panel/HttpIntegrations.tsx (0 hunks)
  • apps/client/src/features/app-settings/panel/integrations-panel/IntegrationsPanel.module.css (0 hunks)
  • apps/client/src/features/app-settings/panel/integrations-panel/IntegrationsPanel.tsx (0 hunks)
  • apps/client/src/features/app-settings/panel/integrations-panel/OscIntegrations.tsx (0 hunks)
  • apps/client/src/features/app-settings/panel/integrations-panel/integrationUtils.ts (0 hunks)
  • apps/client/src/features/app-settings/panel/project-panel/ProjectMergeForm.tsx (3 hunks)
  • apps/client/src/features/app-settings/useAppSettingsMenu.tsx (1 hunks)
  • apps/client/src/theme/ontimeRadio.ts (1 hunks)
  • apps/client/src/theme/theme.ts (2 hunks)
  • apps/server/src/api-data/automation/__tests__/automation.dao.test.ts (1 hunks)
  • apps/server/src/api-data/automation/__tests__/automation.service.test.ts (1 hunks)
  • apps/server/src/api-data/automation/__tests__/automation.utils.test.ts (2 hunks)
  • apps/server/src/api-data/automation/__tests__/testUtils.ts (1 hunks)
  • apps/server/src/api-data/automation/automation.controller.ts (1 hunks)
  • apps/server/src/api-data/automation/automation.dao.ts (1 hunks)
  • apps/server/src/api-data/automation/automation.parser.ts (1 hunks)
  • apps/server/src/api-data/automation/automation.router.ts (1 hunks)
  • apps/server/src/api-data/automation/automation.service.ts (1 hunks)
  • apps/server/src/api-data/automation/automation.utils.ts (3 hunks)
  • apps/server/src/api-data/automation/automation.validation.ts (1 hunks)
  • apps/server/src/api-data/automation/clients/http.client.ts (1 hunks)
  • apps/server/src/api-data/automation/clients/osc.client.ts (1 hunks)
  • apps/server/src/api-data/db/db.controller.ts (1 hunks)
  • apps/server/src/api-data/http/http.controller.ts (0 hunks)
  • apps/server/src/api-data/http/http.router.ts (0 hunks)
  • apps/server/src/api-data/http/http.validation.ts (0 hunks)
  • apps/server/src/api-data/index.ts (2 hunks)
  • apps/server/src/api-data/osc/osc.controller.ts (0 hunks)
  • apps/server/src/api-data/osc/osc.router.ts (0 hunks)
  • apps/server/src/api-data/osc/osc.validation.ts (0 hunks)
  • apps/server/src/api-data/session/session.service.ts (0 hunks)
  • apps/server/src/app.ts (2 hunks)
  • apps/server/src/classes/data-provider/DataProvider.ts (4 hunks)
  • apps/server/src/classes/data-provider/DataProvider.utils.ts (2 hunks)
  • apps/server/src/classes/data-provider/__tests__/DataProvider.utils.test.ts (2 hunks)
  • apps/server/src/models/dataModel.ts (1 hunks)
  • apps/server/src/models/demoProject.ts (1 hunks)
  • apps/server/src/services/integration-service/HttpIntegration.ts (0 hunks)
  • apps/server/src/services/integration-service/IIntegration.ts (0 hunks)
  • apps/server/src/services/integration-service/IntegrationService.ts (0 hunks)
  • apps/server/src/services/integration-service/OscIntegration.ts (0 hunks)
  • apps/server/src/services/project-service/ProjectService.ts (2 hunks)
  • apps/server/src/services/runtime-service/RuntimeService.ts (11 hunks)
  • apps/server/src/stores/EventStore.ts (1 hunks)
  • apps/server/src/stores/__mocks__/runtimeState.mocks.ts (1 hunks)
  • apps/server/src/utils/__tests__/parser.test.ts (1 hunks)
  • apps/server/src/utils/__tests__/parserFunctions.test.ts (0 hunks)
  • apps/server/src/utils/assert.ts (1 hunks)
  • apps/server/src/utils/parser.ts (2 hunks)
  • apps/server/src/utils/parserFunctions.ts (1 hunks)
💤 Files with no reviewable changes (24)
  • apps/client/src/common/models/Info.ts
  • apps/client/src/common/models/Http.ts
  • apps/server/src/api-data/osc/osc.router.ts
  • apps/server/src/api-data/http/http.router.ts
  • apps/client/src/common/models/OscSettings.ts
  • apps/client/src/features/app-settings/panel/integrations-panel/IntegrationsPanel.tsx
  • apps/server/src/api-data/http/http.validation.ts
  • apps/client/src/features/app-settings/panel/integrations-panel/integrationUtils.ts
  • apps/server/src/api-data/session/session.service.ts
  • apps/server/src/services/integration-service/IntegrationService.ts
  • apps/client/src/features/app-settings/panel/integrations-panel/OscIntegrations.tsx
  • apps/server/src/services/integration-service/IIntegration.ts
  • apps/client/src/common/api/osc.ts
  • apps/server/src/services/integration-service/OscIntegration.ts
  • apps/server/src/api-data/osc/osc.validation.ts
  • apps/client/src/features/app-settings/panel/integrations-panel/HttpIntegrations.tsx
  • apps/client/src/common/api/http.ts
  • apps/client/src/features/app-settings/panel/integrations-panel/IntegrationsPanel.module.css
  • apps/server/src/services/integration-service/HttpIntegration.ts
  • apps/server/src/api-data/osc/osc.controller.ts
  • apps/server/src/api-data/http/http.controller.ts
  • apps/server/src/utils/tests/parserFunctions.test.ts
  • apps/client/src/common/hooks-query/useHttpSettings.ts
  • apps/client/src/common/hooks-query/useOscSettings.ts
🧰 Additional context used
🪛 Biome (1.9.4)
apps/server/src/api-data/automation/__tests__/automation.service.test.ts

[error] 27-27: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)

apps/server/src/api-data/automation/__tests__/automation.dao.test.ts

[error] 31-31: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)

apps/server/src/api-data/automation/automation.controller.ts

[error] 59-59: void is confusing inside a union type.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 99-99: void is confusing inside a union type.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)


[error] 109-109: void is confusing inside a union type.

Unsafe fix: Use undefined instead.

(lint/suspicious/noConfusingVoidType)

🔇 Additional comments (37)
apps/server/src/services/project-service/ProjectService.ts (3)

177-177: LGTM! Clean separation of concerns.

The simplified destructuring aligns with the PR's objective of moving integration logic to the new automation service.


247-247: LGTM! Consistent with the changes in loadProjectFile.

The same simplified destructuring is correctly applied here, maintaining consistency across the codebase.


Line range hint 1-300: Verify complete removal of integration-related code.

Let's ensure no integration-related code or references remain in the project service.

✅ Verification successful

Integration-related code has been successfully removed

The project service is now focused solely on project file management operations, with no remaining traces of integration-related code. The only external dependencies are for core functionality like runtime and app state management.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for any remaining integration-related code in the project service

# Check for integration-related imports and usage
rg -i "integration|osc|http" "apps/server/src/services/project-service"

# Check for any remaining integration service references
ast-grep --pattern 'integration' "apps/server/src/services/project-service"

Length of output: 145


Script:

#!/bin/bash
# Broader search for integration-related terms and configurations
rg -i "midi|external|protocol|connector|bridge" "apps/server/src/services/project-service"

# Check imported services and their dependencies
ast-grep --pattern 'import { $_ } from "../$_-service/$_"'

# Look for any remaining configuration or settings
rg -i "config\.|settings\." "apps/server/src/services/project-service"

Length of output: 628

apps/server/src/api-data/index.ts (2)

Line range hint 3-13: LGTM! Verify removal of HTTP and OSC routers.

The addition of the automations router follows the established naming and import patterns.

Let's verify the complete removal of HTTP and OSC related files:

✅ Verification successful

HTTP and OSC routers have been completely removed

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining HTTP/OSC integration files
# Expected: No matches as these should be completely removed

echo "Checking for HTTP integration files..."
fd -e ts -e js "http\.(router|controller|validation|service)" apps/server/src

echo "Checking for OSC integration files..."
fd -e ts -e js "osc\.(router|controller|validation|service)" apps/server/src

Length of output: 330


Script:

#!/bin/bash
# Perform a broader search for any HTTP/OSC related code

echo "Checking for HTTP-related code..."
rg -i "http(router|controller|handler|middleware|config)" apps/server/src

echo "Checking for OSC-related code..."
rg -i "osc(router|controller|handler|middleware|config)" apps/server/src

echo "Checking for specific imports..."
rg -i "import.*['\"].*/(http|osc)/" apps/server/src

Length of output: 433


Line range hint 17-27: LGTM! Consider documenting the architectural changes.

The addition of the /automations endpoint and removal of HTTP/OSC integrations represent a significant architectural shift. Consider updating the API documentation to reflect these changes.

Let's check for existing API documentation that might need updates:

apps/server/src/api-data/automation/clients/http.client.ts (1)

1-7: LGTM! Imports are well-organized and properly typed.

apps/server/src/classes/data-provider/__tests__/DataProvider.utils.test.ts (2)

38-43: LGTM! The automation configuration aligns with the PR objectives.

The new automation configuration structure correctly reflects the transition from integrations to automations, with appropriate default values for the settings.


Line range hint 38-131: Verify the automation model structure.

Let's verify that the automation configuration structure matches the actual model definition.

✅ Verification successful

Automation model structure verified successfully

The automation configuration structure in the test file exactly matches the AutomationSettings type definition from the core types.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for the automation type definition to verify the structure
ast-grep --pattern 'type $_ = {
  enabledAutomations: $_,
  enabledOscIn: $_,
  oscPortIn: $_,
  automations: $_,
  blueprints: $_,
}'

Length of output: 136


Script:

#!/bin/bash
# Find automation-related files
fd -t f automation

# Search for automation type/interface definitions with broader pattern
ast-grep --pattern 'interface Automation {
  $$$
}'

ast-grep --pattern 'type Automation = {
  $$$
}'

# Fallback to ripgrep to find automation type definitions
rg -l "type.*Automation.*=|interface.*Automation.*{" --type ts

Length of output: 2253


Script:

#!/bin/bash
# Check the content of the automation type definitions file
cat packages/types/src/definitions/core/Automation.type.ts

# Search for type containing enabledAutomations
rg "enabledAutomations" --type ts -C 5

Length of output: 21656

apps/server/src/utils/parserFunctions.ts (2)

18-18: LGTM! Import statements are well-organized.

The consolidation of imports and removal of unused OSC/HTTP-related imports aligns well with the PR's objective of rewriting the integrations service.

Also applies to: 22-22


Line range hint 1-285: Verify the complete removal of OSC/HTTP parsing references.

The removal of OSC and HTTP parsing functions appears to be part of the larger restructuring effort. Let's verify that all references to these removed functions have been properly handled.

✅ Verification successful

Removal of OSC/HTTP parsing functions verified successfully

The remaining OSC/HTTP related files are part of the new implementation and don't depend on the removed parsing functions. The codebase shows a clean separation of concerns with the OSC/HTTP functionality properly isolated in the automation system.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for any remaining references to the removed OSC/HTTP parsing functions

# Search for references to removed functions
echo "Searching for references to removed parsing functions..."
rg -l "sanitiseOscSubscriptions|parseOsc|sanitiseHttpSubscriptions|parseHttp" 

# Search for any remaining OSC/HTTP types usage
echo "Searching for OSC/HTTP types usage..."
rg -l "OscSubscription|HttpSubscription|OSCSettings|HttpSettings"

Length of output: 355


Script:

#!/bin/bash
# Broader search for OSC/HTTP related code

# Search for OSC/HTTP related files
echo "Searching for OSC/HTTP related files..."
fd -t f -e ts -e js "osc|http" --ignore-case

# Search for OSC/HTTP related imports and configurations
echo "Searching for OSC/HTTP related code..."
rg -i "(?i)(osc|http).*\b(settings?|subscriptions?|parse|config)\b" -g "*.{ts,js}"

# Search for any remaining imports from removed modules
echo "Searching for imports..."
ast-grep --pattern 'import { $$$ } from $path' | grep -i "osc\|http"

Length of output: 1756


Script:

#!/bin/bash
# Check content of OSC/HTTP related files

echo "Checking OSC adapter content..."
cat "apps/server/src/adapters/OscAdapter.ts"

echo -e "\nChecking OSC/HTTP clients content..."
cat "apps/server/src/api-data/automation/clients/osc.client.ts"
cat "apps/server/src/api-data/automation/clients/http.client.ts"

echo -e "\nChecking OSC parser content..."
cat "apps/server/src/utils/oscArgParser.ts"

Length of output: 6334

apps/client/src/features/app-settings/panel-utils/PanelUtils.module.scss (3)

29-30: LGTM! Good use of shared styles.

The grouping of .section and .indent classes is clean and follows DRY principles.


186-186: LGTM! Consistent spacing.

The padding adjustment aligns well with the spacing system used throughout the styles.


115-119: Verify color contrast for warning state.

While using data attributes for styling is a good practice, please ensure that the $orange-1300 background color provides sufficient contrast with the text color for accessibility compliance.

Run this script to check if there are any color contrast related variables or utilities in the codebase:

✅ Verification successful

Color contrast is appropriate for warning state

The dark orange background (#3D2900) with light text follows accessibility best practices and provides sufficient contrast for warning states in the table.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for color contrast utilities or documentation
rg -i "contrast|a11y|accessibility" --type scss

Length of output: 78


Script:

#!/bin/bash
# Find all SCSS and CSS files
echo "=== Finding SCSS and CSS files ==="
fd -e scss -e css

# Search for orange color variables and related documentation
echo -e "\n=== Searching for orange color definitions ==="
rg "\$orange-|// .*orange" -t css -t scss

# Look for design system or color documentation
echo -e "\n=== Searching for color system documentation ==="
fd -e md -e mdx | xargs rg -i "color.*(system|palette|accessibility)"

Length of output: 7496


Script:

#!/bin/bash
# Check theme files for color definitions
echo "=== Checking theme files for color system ==="
cat "apps/client/src/theme/_ontimeColours.scss"

# Search for orange color usage across files
echo -e "\n=== Searching for orange color usage ==="
rg -l "\$orange" $(fd -e scss)

# Check the specific file for context
echo -e "\n=== Checking PanelUtils.module.scss context ==="
cat "apps/client/src/features/app-settings/panel-utils/PanelUtils.module.scss"

Length of output: 13286

apps/server/src/app.ts (2)

15-15: [Approved] Necessary imports are correctly included.

The imports from ./externals.js are properly adjusted to reflect the changes in the application's architecture. All imported variables (environment, isProduction, updateRouterPrefix) are used appropriately in the code.


132-132: [Approved] Simplified error message enhances clarity.

The updated error message in the checkStart function is concise and focuses on the essential initialization steps, improving readability.

apps/server/src/api-data/automation/automation.service.ts (1)

111-113: Verify that OSC outputs are correctly skipped in Ontime Cloud environments.

In the send function, OSC outputs are conditionally emitted based on the isOntimeCloud flag:

if (!isOntimeCloud) {
  emitOSC(payload, stateSnapshot);
}

Please confirm that this logic aligns with the intended behavior, ensuring that OSC outputs are appropriately handled or skipped when running in Ontime Cloud environments.

✅ Verification successful

OSC outputs are correctly skipped in Ontime Cloud environments

The verification confirms that:

  • The isOntimeCloud flag is properly set via environment variables on the server
  • The OSC emission is consistently guarded by this flag
  • There are no other OSC emission points that could bypass this check
🏁 Scripts executed

The following scripts were executed for the analysis:

Error: No shell scripts found


Script:

#!/bin/bash
# Find the complete send function implementation
ast-grep --pattern 'send($$$) {
  $$$
}'

# Search for isOntimeCloud definition and usage
rg "isOntimeCloud" -A 2

# Find all emitOSC calls
rg "emitOSC\(" -A 2

# Look for Ontime Cloud configuration
rg -i "ontime.*cloud" -A 2

Length of output: 6475

apps/client/src/common/models/AutomationSettings.ts (1)

5-6: Consider relocating OSC input settings to app settings.

Based on previous discussions, OSC input settings such as enabledOscIn and oscPortIn might be better placed within app settings rather than within automationPlaceholderSettings, to maintain separation of concerns between automation configurations and OSC input configurations.

apps/client/src/common/utils/regex.ts (1)

8-8: LGTM! Good improvement to support HTTPS.

The updated regex pattern now correctly handles both HTTP and HTTPS protocols, which is essential for secure connections in the automation service.

apps/client/src/features/app-settings/panel/automations-panel/__tests__/automationUtils.test.ts (1)

16-22: Verify the expected result in the duplicate test case.

The test data shows two entries with title 'Third' (lines 19-20), but the assertion expects [2]. This seems inconsistent with the apparent duplicate entries. Please verify if:

  1. The implementation considers different triggers as unique entries
  2. The test data accurately represents the intended duplicate scenario
  3. The expected result [2] is correct
apps/client/src/common/hooks-query/useAutomationSettings.ts (1)

24-34: LGTM! Well-structured mutation hook.

The mutation implementation follows best practices with proper error handling, optimistic updates via setQueryData, and cache invalidation.

apps/client/src/features/app-settings/AppSettings.tsx (1)

6-6: LGTM! Clean migration from integrations to automation panel.

The changes align well with the PR's objective of renaming integrations to automation, maintaining a clean component structure.

Also applies to: 33-33

apps/client/src/features/app-settings/useAppSettingsMenu.tsx (1)

50-55: LGTM! Well-structured menu options for automation features.

The new automation menu options provide clear navigation paths while maintaining consistency with the existing menu structure.

apps/client/src/features/app-settings/panel/project-panel/ProjectMergeForm.tsx (1)

26-26: LGTM! Clean transition to automation settings.

The changes correctly implement the transition from OSC/HTTP settings to the new automation system while maintaining the existing form structure and validation logic.

Also applies to: 44-44, 116-117

apps/client/src/theme/theme.ts (1)

17-17: LGTM! Proper theme configuration.

The new radio variant is correctly implemented following Chakra UI's theming patterns.

Also applies to: 112-112

apps/server/src/classes/data-provider/DataProvider.ts (1)

121-128: LGTM! Consistent implementation pattern.

The new automation settings functions follow the established pattern of other settings management functions, maintaining type safety and immutability.

apps/client/src/features/app-settings/panel-utils/PanelUtils.tsx (1)

60-60: LGTM!

The className composition using the cx utility is implemented correctly.

apps/client/src/features/app-settings/panel/automations-panel/BlueprintsList.tsx (1)

1-24: LGTM! Clean imports and well-defined props interface.

The component's dependencies and type definitions are properly structured.

apps/server/src/api-data/automation/__tests__/automation.utils.test.ts (1)

1-1: LGTM! Import statement updated correctly.

The import statement has been properly updated to use the local automation utils module.

apps/server/src/api-data/db/db.controller.ts (1)

21-30: LGTM! Property replacement aligns with architecture changes.

The replacement of http with automation in the request body destructuring and patchDb object aligns with the PR's objective of transitioning from integrations to automation.

apps/server/src/models/demoProject.ts (1)

453-458: Verify default automation settings.

The default settings look reasonable, but please verify:

  1. Is enabledAutomations: false the intended default for demo projects?
  2. Should oscPortIn: 8888 be configurable rather than hardcoded?

Run this script to check for other occurrences of the port number:

✅ Verification successful

Default automation settings are correct and port configuration is intentional

The verification confirms:

  • enabledAutomations: false is the intended safe default for demo projects
  • Port 8888 is standardized for OSC input across the application and explicitly configured in Docker setup
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for hardcoded port 8888
rg -l "8888"

Length of output: 506


Script:

#!/bin/bash
# Check for environment variables or configuration related to port 8888
rg -A 5 "port.*8888|8888.*port" --type ts --type yml --type env

# Check AutomationSettings.ts content
cat apps/client/src/common/models/AutomationSettings.ts

# Look for demo project initialization patterns
rg -A 5 "demo.*project.*default|default.*demo.*project" --type ts

Length of output: 449


Script:

#!/bin/bash
# Check docker and yaml files for port configuration
rg -A 5 "port.*8888|8888.*port" docker-compose.yml Dockerfile

# Look for environment variable patterns for port configuration
rg -A 5 "PORT|OSC_PORT|AUTOMATION_PORT" --type ts --type env

# Check for any configuration files
fd -e yaml -e yml -e json -e config

Length of output: 628


Script:

#!/bin/bash
# Check test db and server package.json for configuration
cat apps/server/test-db/db.json
cat apps/server/package.json

# Check docker-compose for port configuration
cat docker-compose.yml

# One final check for environment variable patterns in TypeScript files
rg "process\.env\." --type ts -A 2

Length of output: 14245

apps/server/src/utils/parser.ts (2)

25-28: LGTM! Import changes align with the automation service refactor.

The changes correctly reflect the transition from integrations to automation by:

  1. Removing HTTP and OSC imports
  2. Adding automation settings parser import

33-33: LGTM! Well-defined error handling type.

The ErrorEmitter type enhances error handling capabilities by providing a clear contract for error reporting functions.

apps/server/src/services/runtime-service/RuntimeService.ts (1)

78-82: LGTM! Consistent migration from integrations to automations.

The changes systematically replace integrationService.dispatch with triggerAutomations across all timer lifecycle events:

  • Warning phase
  • Danger phase
  • Timer finish
  • Timer update
  • Clock update
  • Event load
  • Timer start
  • Timer pause
  • Timer stop
  • Timer roll

The migration maintains the same event handling flow while transitioning to the new automation system.

Also applies to: 98-98, 118-118, 138-138, 148-148, 295-295, 474-474, 527-527, 546-546, 594-604

apps/server/src/utils/__tests__/parser.test.ts (1)

317-317: Verify the reduction in expected error count.

The test now expects 5 errors instead of 7 for duplicate IDs. Please confirm if this change is intentional and aligns with the new error handling logic in the parser.

✅ Verification successful

The error count change is intentional and correct

The test case "detects duplicate Ids" is specifically testing imports outside the domain, and the change from 7 to 5 errors is correct. The test intentionally provides malformed data with duplicate IDs and missing required sections, and the parser correctly identifies 5 validation errors across different contexts (Rundown, Project, View Settings, etc.).

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for related changes in error handling logic

# Search for error-related code in the parser
rg -A 5 "errors\.push|makeEmitError" apps/server/src/utils/parser.ts

Length of output: 865


Script:

#!/bin/bash
# Search for duplicate ID related code and validation
rg -B 2 -A 5 "duplicate.*id" apps/server/src/utils/parser.ts

# Get the complete test case context
rg -B 10 -A 10 "expect\(errors\.length\)\.toBe\(5\)" apps/server/src/utils/__tests__/parser.test.ts

Length of output: 705


Script:

#!/bin/bash
# Get the complete test case
ast-grep --pattern 'it($_, () => {
  $$$
  expect(errors.length).toBe(5);
  $$$
})' apps/server/src/utils/__tests__/parser.test.ts

# Search for validation rules in parser
rg -B 5 -A 5 "validate|check" apps/server/src/utils/parser.ts

Length of output: 5267


Script:

#!/bin/bash
# Search for required fields and their validation
rg -B 5 -A 5 "required" apps/server/src/utils/parser.ts

# Get the test file's required settings
rg -B 2 -A 2 "requiredSettings" apps/server/src/utils/__tests__/parser.test.ts

Length of output: 783


Script:

#!/bin/bash
# Get the parser implementation focusing on validation and error handling
ast-grep --pattern 'function parseDatabaseModel($_) {
  $$$
}' apps/server/src/utils/parser.ts

# Search for any error messages in the parser
rg "Error.*:|error.*:" apps/server/src/utils/parser.ts

Length of output: 508

apps/client/src/features/app-settings/panel-content/PanelContent.module.scss (1)

20-20: Verify the large padding value.

A padding-bottom of 300px seems unusually large. Please confirm if this size is necessary to accommodate the new automation UI components without causing content overlap.

✅ Verification successful

The 300px bottom padding is appropriate for this component

The padding is justified as the component renders multiple different panels (Automation, Sources, Network, etc.) with varying content heights in a scrollable container. This extra space ensures content remains accessible and prevents awkward cutoffs at the bottom of the scrollable area.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for related UI components that might require this padding

# Search for components using this class
rg -A 5 "PanelContent_content" apps/client/src/features/app-settings/

Length of output: 69


Script:

#!/bin/bash
# Find the module file and its references
echo "=== Searching for PanelContent.module.scss ==="
fd "PanelContent.module.scss" apps/client/src/

echo -e "\n=== Finding references to PanelContent component ==="
rg -l "PanelContent" apps/client/src/features/app-settings/

echo -e "\n=== Checking content of PanelContent component ==="
rg -A 10 "class.*PanelContent" apps/client/src/features/app-settings/

Length of output: 719


Script:

#!/bin/bash
echo "=== PanelContent.tsx Implementation ==="
cat apps/client/src/features/app-settings/panel-content/PanelContent.tsx

echo -e "\n=== AppSettings.tsx Usage ==="
rg -A 5 -B 5 "PanelContent" apps/client/src/features/app-settings/AppSettings.tsx

echo -e "\n=== Checking for scroll/overflow styles ==="
rg "overflow|scroll" apps/client/src/features/app-settings/panel-content/PanelContent.module.scss

Length of output: 2661

apps/client/src/features/app-settings/panel/automations-panel/BlueprintForm.module.scss (1)

1-14: LGTM! Well-structured layout classes.

The .outerColumn and .innerColumn classes provide a clean and consistent layout structure with appropriate spacing.

apps/client/src/common/utils/__tests__/regex.test.ts (1)

29-30: Verify regex implementation matches test requirements.

Let's verify that the regex implementation in the codebase properly handles both HTTP and HTTPS URLs.

✅ Verification successful

Regex implementation correctly validates HTTP/HTTPS URLs

The regex pattern /^https?:\/\// properly matches the test cases, validating URLs that start with either "http://" or "https://" while rejecting invalid formats.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find and verify the regex implementation for URL validation

# Search for the regex pattern definition
echo "Searching for startsWithHttp regex definition:"
rg -l "startsWithHttp.*=" src/

Length of output: 180


Script:

#!/bin/bash
# Search for regex implementation and tests
echo "Searching for regex files:"
fd regex "apps/client/src"

echo -e "\nChecking content of relevant files:"
for f in $(fd regex "apps/client/src"); do
  echo -e "\n=== Content of $f ==="
  cat "$f"
done

Length of output: 3252

Comment on lines +22 to +44
/** Emits message over transport */
async function emit(url: string) {
logger.info(LogOrigin.Rx, `Sending HTTP: ${url}`);
try {
const response = await fetch(url);
if (!response.ok) {
if (response.status >= 500 && response.status < 600) {
logger.warning(LogOrigin.Tx, `HTTP Integration: Server refused message ${response.status}`);
} else if (response.status >= 400) {
logger.warning(LogOrigin.Tx, `HTTP Integration: Failed sending message ${response.status}`);
} else {
logger.warning(LogOrigin.Tx, `HTTP Integration: Failed sending message ${response.status}`);
}
}
} catch (error) {
if (!(error instanceof Error)) {
logger.warning(LogOrigin.Tx, `HTTP Integration: Failed sending message ${error}`);
return;
}

logger.warning(LogOrigin.Tx, `HTTP Integration: ${error.name} ${error.message}`);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance HTTP client robustness and error handling.

Several improvements could make the HTTP client more robust:

  1. The else clause (lines 32-34) duplicates the 400-level error message
  2. Missing timeout handling for fetch requests
  3. Could benefit from response body logging for debugging
  4. No retry mechanism for transient failures
  5. Missing abort controller for request cancellation
-async function emit(url: string) {
+async function emit(url: string, options = { timeout: 5000, retries: 3 }) {
   logger.info(LogOrigin.Rx, `Sending HTTP: ${url}`);
+  const controller = new AbortController();
+  const timeoutId = setTimeout(() => controller.abort(), options.timeout);
+
+  let attempts = 0;
+  while (attempts < options.retries) {
     try {
-      const response = await fetch(url);
+      const response = await fetch(url, { signal: controller.signal });
+      clearTimeout(timeoutId);
+
       if (!response.ok) {
+        const responseText = await response.text();
+        logger.debug(LogOrigin.Tx, `Response body: ${responseText}`);
+
         if (response.status >= 500 && response.status < 600) {
           logger.warning(LogOrigin.Tx, `HTTP Integration: Server refused message ${response.status}`);
+          // Retry server errors
+          attempts++;
+          if (attempts < options.retries) {
+            await new Promise(resolve => setTimeout(resolve, 1000 * attempts));
+            continue;
+          }
         } else if (response.status >= 400) {
           logger.warning(LogOrigin.Tx, `HTTP Integration: Failed sending message ${response.status}`);
-        } else {
-          logger.warning(LogOrigin.Tx, `HTTP Integration: Failed sending message ${response.status}`);
         }
       }
+      break;
     } catch (error) {
+      clearTimeout(timeoutId);
+
       if (!(error instanceof Error)) {
         logger.warning(LogOrigin.Tx, `HTTP Integration: Failed sending message ${error}`);
         return;
       }
 
+      if (error.name === 'AbortError') {
+        logger.warning(LogOrigin.Tx, `HTTP Integration: Request timed out after ${options.timeout}ms`);
+        return;
+      }
+
       logger.warning(LogOrigin.Tx, `HTTP Integration: ${error.name} ${error.message}`);
+      
+      // Retry network errors
+      attempts++;
+      if (attempts < options.retries) {
+        await new Promise(resolve => setTimeout(resolve, 1000 * attempts));
+        continue;
+      }
+      break;
     }
+  }
 }
📝 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.

Suggested change
/** Emits message over transport */
async function emit(url: string) {
logger.info(LogOrigin.Rx, `Sending HTTP: ${url}`);
try {
const response = await fetch(url);
if (!response.ok) {
if (response.status >= 500 && response.status < 600) {
logger.warning(LogOrigin.Tx, `HTTP Integration: Server refused message ${response.status}`);
} else if (response.status >= 400) {
logger.warning(LogOrigin.Tx, `HTTP Integration: Failed sending message ${response.status}`);
} else {
logger.warning(LogOrigin.Tx, `HTTP Integration: Failed sending message ${response.status}`);
}
}
} catch (error) {
if (!(error instanceof Error)) {
logger.warning(LogOrigin.Tx, `HTTP Integration: Failed sending message ${error}`);
return;
}
logger.warning(LogOrigin.Tx, `HTTP Integration: ${error.name} ${error.message}`);
}
}
/** Emits message over transport */
async function emit(url: string, options = { timeout: 5000, retries: 3 }) {
logger.info(LogOrigin.Rx, `Sending HTTP: ${url}`);
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), options.timeout);
let attempts = 0;
while (attempts < options.retries) {
try {
const response = await fetch(url, { signal: controller.signal });
clearTimeout(timeoutId);
if (!response.ok) {
const responseText = await response.text();
logger.debug(LogOrigin.Tx, `Response body: ${responseText}`);
if (response.status >= 500 && response.status < 600) {
logger.warning(LogOrigin.Tx, `HTTP Integration: Server refused message ${response.status}`);
// Retry server errors
attempts++;
if (attempts < options.retries) {
await new Promise(resolve => setTimeout(resolve, 1000 * attempts));
continue;
}
} else if (response.status >= 400) {
logger.warning(LogOrigin.Tx, `HTTP Integration: Failed sending message ${response.status}`);
}
}
break;
} catch (error) {
clearTimeout(timeoutId);
if (!(error instanceof Error)) {
logger.warning(LogOrigin.Tx, `HTTP Integration: Failed sending message ${error}`);
return;
}
if (error.name === 'AbortError') {
logger.warning(LogOrigin.Tx, `HTTP Integration: Request timed out after ${options.timeout}ms`);
return;
}
logger.warning(LogOrigin.Tx, `HTTP Integration: ${error.name} ${error.message}`);
// Retry network errors
attempts++;
if (attempts < options.retries) {
await new Promise(resolve => setTimeout(resolve, 1000 * attempts));
continue;
}
break;
}
}
}

apps/server/src/api-data/automation/automation.router.ts Outdated Show resolved Hide resolved
apps/server/src/api-data/automation/automation.service.ts Outdated Show resolved Hide resolved
apps/server/src/api-data/automation/automation.utils.ts Outdated Show resolved Hide resolved
@cpvalente cpvalente force-pushed the integrations branch 4 times, most recently from a5663da to 97d34bf Compare January 20, 2025 18:33
@cpvalente
Copy link
Owner Author

@alex-Arc , I believe this is ready to merge, would you mind sanity checking?

@@ -26,8 +26,8 @@ describe('simple tests for regex', () => {
});

test('startsWithHttp', () => {
const right = ['http://test'];
const wrong = ['https://test', 'testing', '123.0.1'];
const right = ['https://test', 'http://test'];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you get https working?
or did it always work we just did not allow it

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i have been testing with ontime cloud and it seems fine. can you verify in one of your containers?

@@ -283,7 +257,6 @@ export const shutdown = async (exitCode = 0) => {

expressServer?.close();
runtimeService.shutdown();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we not still need the osc shutdown here?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes we do, good catch

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants