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;
|
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(
|
export async function fetchConnectionServices(
|
||||||
request: ApiRequestFunction,
|
request: ApiRequestFunction,
|
||||||
instanceId: string,
|
instanceId: string,
|
||||||
connectionId: string
|
connectionId: string
|
||||||
): Promise<ConnectionService[]> {
|
): Promise<ConnectionService[]> {
|
||||||
const data = await request({
|
const data = await request({
|
||||||
url: `/api/workflows/${instanceId}/connections/${connectionId}/services`,
|
url: `/api/workflows/${instanceId}/connections/${_encodedConnectionId(connectionId)}/services`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
});
|
});
|
||||||
return data?.services ?? [];
|
return data?.services ?? [];
|
||||||
|
|
@ -972,7 +977,7 @@ export async function fetchBrowse(
|
||||||
path = '/'
|
path = '/'
|
||||||
): Promise<{ items: BrowseEntry[]; path: string; service: string }> {
|
): Promise<{ items: BrowseEntry[]; path: string; service: string }> {
|
||||||
const data = await request({
|
const data = await request({
|
||||||
url: `/api/workflows/${instanceId}/connections/${connectionId}/browse`,
|
url: `/api/workflows/${instanceId}/connections/${_encodedConnectionId(connectionId)}/browse`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: { service, path },
|
params: { service, path },
|
||||||
});
|
});
|
||||||
|
|
@ -986,7 +991,7 @@ export async function fetchClickupTask(
|
||||||
taskId: string
|
taskId: string
|
||||||
): Promise<Record<string, unknown>> {
|
): Promise<Record<string, unknown>> {
|
||||||
const data = await request({
|
const data = await request({
|
||||||
url: `/api/clickup/${connectionId}/tasks/${encodeURIComponent(taskId)}`,
|
url: `/api/clickup/${_encodedConnectionId(connectionId)}/tasks/${encodeURIComponent(taskId)}`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
});
|
});
|
||||||
return data && typeof data === 'object' ? (data as Record<string, unknown>) : {};
|
return data && typeof data === 'object' ? (data as Record<string, unknown>) : {};
|
||||||
|
|
@ -999,7 +1004,7 @@ export async function fetchClickupList(
|
||||||
listId: string
|
listId: string
|
||||||
): Promise<Record<string, unknown>> {
|
): Promise<Record<string, unknown>> {
|
||||||
const data = await request({
|
const data = await request({
|
||||||
url: `/api/clickup/${connectionId}/lists/${listId}`,
|
url: `/api/clickup/${_encodedConnectionId(connectionId)}/lists/${encodeURIComponent(listId)}`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
});
|
});
|
||||||
return data && typeof data === 'object' ? (data as Record<string, unknown>) : {};
|
return data && typeof data === 'object' ? (data as Record<string, unknown>) : {};
|
||||||
|
|
@ -1012,7 +1017,7 @@ export async function fetchClickupTeam(
|
||||||
teamId: string
|
teamId: string
|
||||||
): Promise<Record<string, unknown>> {
|
): Promise<Record<string, unknown>> {
|
||||||
const data = await request({
|
const data = await request({
|
||||||
url: `/api/clickup/${connectionId}/teams/${teamId}`,
|
url: `/api/clickup/${_encodedConnectionId(connectionId)}/teams/${encodeURIComponent(teamId)}`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
});
|
});
|
||||||
return data && typeof data === 'object' ? (data as Record<string, unknown>) : {};
|
return data && typeof data === 'object' ? (data as Record<string, unknown>) : {};
|
||||||
|
|
@ -1025,7 +1030,7 @@ export async function fetchClickupListFields(
|
||||||
listId: string
|
listId: string
|
||||||
): Promise<{ fields?: unknown[] } & Record<string, unknown>> {
|
): Promise<{ fields?: unknown[] } & Record<string, unknown>> {
|
||||||
const data = await request({
|
const data = await request({
|
||||||
url: `/api/clickup/${connectionId}/lists/${listId}/fields`,
|
url: `/api/clickup/${_encodedConnectionId(connectionId)}/lists/${encodeURIComponent(listId)}/fields`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
});
|
});
|
||||||
return (data && typeof data === 'object' ? data : {}) as { fields?: unknown[] } & Record<string, unknown>;
|
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>
|
{ tasks?: ClickupListTaskItem[]; last_page?: boolean } & Record<string, unknown>
|
||||||
> {
|
> {
|
||||||
const data = await request({
|
const data = await request({
|
||||||
url: `/api/clickup/${connectionId}/lists/${listId}/tasks`,
|
url: `/api/clickup/${_encodedConnectionId(connectionId)}/lists/${encodeURIComponent(listId)}/tasks`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: {
|
params: {
|
||||||
page: options?.page ?? 0,
|
page: options?.page ?? 0,
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ export function formatApiError(error: any, defaultMessage: string): string {
|
||||||
// Handle other error formats
|
// Handle other error formats
|
||||||
if (typeof data?.detail === 'string') return data.detail;
|
if (typeof data?.detail === 'string') return data.detail;
|
||||||
if (typeof data?.message === 'string') return data.message;
|
if (typeof data?.message === 'string') return data.message;
|
||||||
|
if (typeof data?.error === 'string') return data.error;
|
||||||
if (typeof data === 'string') return data;
|
if (typeof data === 'string') return data;
|
||||||
|
|
||||||
return defaultMessage;
|
return defaultMessage;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue