fix(flow-editor): encode connection id in browse and ClickUp API paths
All checks were successful
Deploy Nyla Frontend to Integration / deploy (push) Successful in 1m17s
All checks were successful
Deploy Nyla Frontend to Integration / deploy (push) Successful in 1m17s
URL-encode connection references with spaces/colons so ClickUp list browse resolves correctly. Surface browse error field in formatApiError. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
b36b303def
commit
059bbe956a
3 changed files with 30 additions and 7 deletions
17
src/api/workflowApi.connectionPath.test.ts
Normal file
17
src/api/workflowApi.connectionPath.test.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
/** Mirrors _encodedConnectionId in workflowApi.ts for browse/services URL paths. */
|
||||
function encodedConnectionId(connectionId: string): string {
|
||||
return encodeURIComponent(connectionId);
|
||||
}
|
||||
|
||||
describe('connection path encoding for workflow browse', () => {
|
||||
it('encodes spaces and colons in connection:clickup:username references', () => {
|
||||
const ref = 'connection:clickup:Stephan Schellworth';
|
||||
const segment = encodedConnectionId(ref);
|
||||
expect(segment).toBe('connection%3Aclickup%3AStephan%20Schellworth');
|
||||
const url = `/api/workflows/inst/connections/${segment}/browse`;
|
||||
expect(url).not.toContain(' ');
|
||||
expect(url).toContain('%20');
|
||||
});
|
||||
});
|
||||
|
|
@ -943,13 +943,18 @@ export interface ConnectionService {
|
|||
icon: string;
|
||||
}
|
||||
|
||||
/** Encode connection id/reference for URL path segments (may contain spaces/colons). */
|
||||
function _encodedConnectionId(connectionId: string): string {
|
||||
return encodeURIComponent(connectionId);
|
||||
}
|
||||
|
||||
export async function fetchConnectionServices(
|
||||
request: ApiRequestFunction,
|
||||
instanceId: string,
|
||||
connectionId: string
|
||||
): Promise<ConnectionService[]> {
|
||||
const data = await request({
|
||||
url: `/api/workflows/${instanceId}/connections/${connectionId}/services`,
|
||||
url: `/api/workflows/${instanceId}/connections/${_encodedConnectionId(connectionId)}/services`,
|
||||
method: 'get',
|
||||
});
|
||||
return data?.services ?? [];
|
||||
|
|
@ -972,7 +977,7 @@ export async function fetchBrowse(
|
|||
path = '/'
|
||||
): Promise<{ items: BrowseEntry[]; path: string; service: string }> {
|
||||
const data = await request({
|
||||
url: `/api/workflows/${instanceId}/connections/${connectionId}/browse`,
|
||||
url: `/api/workflows/${instanceId}/connections/${_encodedConnectionId(connectionId)}/browse`,
|
||||
method: 'get',
|
||||
params: { service, path },
|
||||
});
|
||||
|
|
@ -986,7 +991,7 @@ export async function fetchClickupTask(
|
|||
taskId: string
|
||||
): Promise<Record<string, unknown>> {
|
||||
const data = await request({
|
||||
url: `/api/clickup/${connectionId}/tasks/${encodeURIComponent(taskId)}`,
|
||||
url: `/api/clickup/${_encodedConnectionId(connectionId)}/tasks/${encodeURIComponent(taskId)}`,
|
||||
method: 'get',
|
||||
});
|
||||
return data && typeof data === 'object' ? (data as Record<string, unknown>) : {};
|
||||
|
|
@ -999,7 +1004,7 @@ export async function fetchClickupList(
|
|||
listId: string
|
||||
): Promise<Record<string, unknown>> {
|
||||
const data = await request({
|
||||
url: `/api/clickup/${connectionId}/lists/${listId}`,
|
||||
url: `/api/clickup/${_encodedConnectionId(connectionId)}/lists/${encodeURIComponent(listId)}`,
|
||||
method: 'get',
|
||||
});
|
||||
return data && typeof data === 'object' ? (data as Record<string, unknown>) : {};
|
||||
|
|
@ -1012,7 +1017,7 @@ export async function fetchClickupTeam(
|
|||
teamId: string
|
||||
): Promise<Record<string, unknown>> {
|
||||
const data = await request({
|
||||
url: `/api/clickup/${connectionId}/teams/${teamId}`,
|
||||
url: `/api/clickup/${_encodedConnectionId(connectionId)}/teams/${encodeURIComponent(teamId)}`,
|
||||
method: 'get',
|
||||
});
|
||||
return data && typeof data === 'object' ? (data as Record<string, unknown>) : {};
|
||||
|
|
@ -1025,7 +1030,7 @@ export async function fetchClickupListFields(
|
|||
listId: string
|
||||
): Promise<{ fields?: unknown[] } & Record<string, unknown>> {
|
||||
const data = await request({
|
||||
url: `/api/clickup/${connectionId}/lists/${listId}/fields`,
|
||||
url: `/api/clickup/${_encodedConnectionId(connectionId)}/lists/${encodeURIComponent(listId)}/fields`,
|
||||
method: 'get',
|
||||
});
|
||||
return (data && typeof data === 'object' ? data : {}) as { fields?: unknown[] } & Record<string, unknown>;
|
||||
|
|
@ -1046,7 +1051,7 @@ export async function fetchClickupListTasks(
|
|||
{ tasks?: ClickupListTaskItem[]; last_page?: boolean } & Record<string, unknown>
|
||||
> {
|
||||
const data = await request({
|
||||
url: `/api/clickup/${connectionId}/lists/${listId}/tasks`,
|
||||
url: `/api/clickup/${_encodedConnectionId(connectionId)}/lists/${encodeURIComponent(listId)}/tasks`,
|
||||
method: 'get',
|
||||
params: {
|
||||
page: options?.page ?? 0,
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ export function formatApiError(error: any, defaultMessage: string): string {
|
|||
// Handle other error formats
|
||||
if (typeof data?.detail === 'string') return data.detail;
|
||||
if (typeof data?.message === 'string') return data.message;
|
||||
if (typeof data?.error === 'string') return data.error;
|
||||
if (typeof data === 'string') return data;
|
||||
|
||||
return defaultMessage;
|
||||
|
|
|
|||
Loading…
Reference in a new issue