Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/agent/src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,8 @@ function getAiRoutes(options: Options, services: Services, aiRouter: AiRouter |
}

function getWorkflowExecutorRoutes(options: Options, services: Services): BaseRoute[] {
if (!options.workflowExecutorUrl) return [];

// Always mount the route so that hitting it without `workflowExecutorUrl` configured
// returns an explicit error to the client instead of a bare 404.
return [new WorkflowExecutorProxyRoute(services, options)];
}

Expand Down
13 changes: 11 additions & 2 deletions packages/agent/src/routes/workflow/workflow-executor-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { AgentOptionsWithDefaults } from '../../types';
import type KoaRouter from '@koa/router';
import type { Context } from 'koa';

import { InternalServerError } from '@forestadmin/datasource-toolkit';
import { request as httpRequest } from 'http';
import { request as httpsRequest } from 'https';

Expand All @@ -16,12 +17,14 @@ type ForwardedHeaders = {

export default class WorkflowExecutorProxyRoute extends BaseRoute {
readonly type = RouteType.PrivateRoute;
private readonly executorUrl: URL;
private readonly executorUrl: URL | null;

constructor(services: ForestAdminHttpDriverServices, options: AgentOptionsWithDefaults) {
super(services, options);
// Remove trailing slash for clean URL joining
this.executorUrl = new URL(options.workflowExecutorUrl.replace(/\/+$/, ''));
this.executorUrl = options.workflowExecutorUrl
? new URL(options.workflowExecutorUrl.replace(/\/+$/, ''))
: null;
}

setupRoutes(router: KoaRouter): void {
Expand All @@ -30,6 +33,12 @@ export default class WorkflowExecutorProxyRoute extends BaseRoute {
}

private async handleProxy(context: Context): Promise<void> {
if (!this.executorUrl) {
throw new InternalServerError(
'The workflow executor is not configured on this agent: the `workflowExecutorUrl` option is missing.',
);
}

const { runId } = context.params;
const isTrigger = context.method === 'POST';
const qs = context.querystring ? `?${context.querystring}` : '';
Expand Down
6 changes: 5 additions & 1 deletion packages/agent/test/routes/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,12 @@ describe('Route index', () => {
expect(NATIVE_QUERY_ROUTES_CTOR).toEqual([DataSourceNativeQueryRoute]);
});

const WORKFLOW_EXECUTOR_ROUTE_SIZE = 1;
const BASE_ROUTE_SIZE =
ROOT_ROUTES_CTOR.length + CAPABILITIES_ROUTES_CTOR.length + NATIVE_QUERY_ROUTES_CTOR.length;
ROOT_ROUTES_CTOR.length +
CAPABILITIES_ROUTES_CTOR.length +
NATIVE_QUERY_ROUTES_CTOR.length +
WORKFLOW_EXECUTOR_ROUTE_SIZE;

describe('makeRoutes', () => {
describe('when a data source without relations', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@ describe('WorkflowExecutorProxyRoute', () => {

expect(route.type).toBe(RouteType.PrivateRoute);
});

test('should not throw when workflowExecutorUrl is not configured', () => {
expect(
() =>
new WorkflowExecutorProxyRoute(
services,
factories.forestAdminHttpDriverOptions.build({ workflowExecutorUrl: null }),
),
).not.toThrow();
});
});

describe('setupRoutes', () => {
Expand Down Expand Up @@ -224,6 +234,24 @@ describe('WorkflowExecutorProxyRoute', () => {
).rejects.toThrow();
});

test('should throw an explicit error when workflowExecutorUrl is not configured', async () => {
const route = new WorkflowExecutorProxyRoute(
services,
factories.forestAdminHttpDriverOptions.build({ workflowExecutorUrl: null }),
);

const context = createMockContext({
customProperties: { params: { runId: 'run-123' } },
});
Object.defineProperty(context, 'url', {
value: '/_internal/workflow-executions/run-123',
});

await expect(
(route as unknown as { handleProxy: (ctx: unknown) => Promise<void> }).handleProxy(context),
).rejects.toThrow('The workflow executor is not configured on this agent');
});

test('should forward query params to the executor', async () => {
const route = new WorkflowExecutorProxyRoute(
services,
Expand Down
Loading