@shipfox/client-projects 5.0.0 → 6.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +57 -0
  3. package/dist/chrome.d.ts.map +1 -1
  4. package/dist/chrome.js +7 -10
  5. package/dist/chrome.js.map +1 -1
  6. package/dist/components/model-provider-reminder-banner.d.ts.map +1 -1
  7. package/dist/components/model-provider-reminder-banner.js +1 -1
  8. package/dist/components/model-provider-reminder-banner.js.map +1 -1
  9. package/dist/components/source-strip.d.ts +2 -2
  10. package/dist/components/source-strip.d.ts.map +1 -1
  11. package/dist/components/source-strip.js +2 -2
  12. package/dist/components/source-strip.js.map +1 -1
  13. package/dist/core/definition.d.ts +32 -0
  14. package/dist/core/definition.d.ts.map +1 -0
  15. package/dist/core/definition.js +3 -0
  16. package/dist/core/definition.js.map +1 -0
  17. package/dist/core/project.d.ts +26 -0
  18. package/dist/core/project.d.ts.map +1 -0
  19. package/dist/core/project.js +12 -0
  20. package/dist/core/project.js.map +1 -0
  21. package/dist/feature.d.ts +8 -0
  22. package/dist/feature.d.ts.map +1 -1
  23. package/dist/feature.js +11 -10
  24. package/dist/feature.js.map +1 -1
  25. package/dist/hooks/api/definitions.d.ts +12 -88
  26. package/dist/hooks/api/definitions.d.ts.map +1 -1
  27. package/dist/hooks/api/definitions.js +18 -10
  28. package/dist/hooks/api/definitions.js.map +1 -1
  29. package/dist/hooks/api/mappers.d.ts +17 -0
  30. package/dist/hooks/api/mappers.d.ts.map +1 -0
  31. package/dist/hooks/api/mappers.js +56 -0
  32. package/dist/hooks/api/mappers.js.map +1 -0
  33. package/dist/hooks/api/projects.d.ts +18 -84
  34. package/dist/hooks/api/projects.d.ts.map +1 -1
  35. package/dist/hooks/api/projects.js +72 -29
  36. package/dist/hooks/api/projects.js.map +1 -1
  37. package/dist/index.d.ts +2 -0
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +1 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/pages/create-project-page.d.ts.map +1 -1
  42. package/dist/pages/create-project-page.js +12 -37
  43. package/dist/pages/create-project-page.js.map +1 -1
  44. package/dist/pages/create-project-page.stories.js +3 -1
  45. package/dist/pages/create-project-page.stories.js.map +1 -1
  46. package/dist/pages/home-router.d.ts +4 -1
  47. package/dist/pages/home-router.d.ts.map +1 -1
  48. package/dist/pages/home-router.js +4 -2
  49. package/dist/pages/home-router.js.map +1 -1
  50. package/dist/pages/projects-hub-page.d.ts +3 -1
  51. package/dist/pages/projects-hub-page.d.ts.map +1 -1
  52. package/dist/pages/projects-hub-page.js +26 -30
  53. package/dist/pages/projects-hub-page.js.map +1 -1
  54. package/dist/routes/home.d.ts +3 -2
  55. package/dist/routes/home.d.ts.map +1 -1
  56. package/dist/routes/home.js +7 -2
  57. package/dist/routes/home.js.map +1 -1
  58. package/dist/routes/inputs.d.ts +6 -0
  59. package/dist/routes/inputs.d.ts.map +1 -0
  60. package/dist/routes/inputs.js +14 -0
  61. package/dist/routes/inputs.js.map +1 -0
  62. package/dist/routes/search.d.ts +5 -0
  63. package/dist/routes/search.d.ts.map +1 -0
  64. package/dist/routes/search.js +8 -0
  65. package/dist/routes/search.js.map +1 -0
  66. package/dist/tsconfig.test.tsbuildinfo +1 -1
  67. package/package.json +14 -15
  68. package/src/chrome.tsx +11 -6
  69. package/src/components/model-provider-reminder-banner.tsx +1 -3
  70. package/src/components/project-switcher.test.tsx +2 -1
  71. package/src/components/source-strip.tsx +5 -5
  72. package/src/core/definition.ts +31 -0
  73. package/src/core/project.ts +47 -0
  74. package/src/feature.ts +13 -11
  75. package/src/hooks/api/definitions.ts +54 -19
  76. package/src/hooks/api/mappers.ts +67 -0
  77. package/src/hooks/api/projects.test.ts +11 -4
  78. package/src/hooks/api/projects.ts +114 -39
  79. package/src/index.ts +9 -0
  80. package/src/pages/create-project-page.stories.tsx +6 -1
  81. package/src/pages/create-project-page.test.tsx +3 -1
  82. package/src/pages/create-project-page.tsx +16 -51
  83. package/src/pages/home-router.tsx +3 -2
  84. package/src/pages/projects-hub-page.test.tsx +13 -8
  85. package/src/pages/projects-hub-page.tsx +24 -34
  86. package/src/routes/home.tsx +6 -2
  87. package/src/routes/inputs.test.ts +14 -0
  88. package/src/routes/inputs.ts +15 -0
  89. package/src/routes/search.ts +8 -0
  90. package/tsconfig.build.tsbuildinfo +1 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shipfox/client-projects",
3
3
  "license": "MIT",
4
- "version": "5.0.0",
4
+ "version": "6.0.1",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/ShipfoxHQ/shipfox.git",
@@ -10,10 +10,6 @@
10
10
  "type": "module",
11
11
  "main": "dist/index.js",
12
12
  "types": "dist/index.d.ts",
13
- "imports": {
14
- "#*": "./dist/*",
15
- "#test/*": "./test/*"
16
- },
17
13
  "exports": {
18
14
  ".": {
19
15
  "types": "./dist/index.d.ts",
@@ -31,16 +27,16 @@
31
27
  "dependencies": {
32
28
  "@swc/helpers": "^0.5.17",
33
29
  "@tanstack/react-form": "^1.32.0",
34
- "@shipfox/api-definitions-dto": "6.0.0",
35
- "@shipfox/api-integration-core-dto": "8.0.0",
36
- "@shipfox/api-projects-dto": "8.0.0",
37
- "@shipfox/client-agent": "5.0.0",
38
- "@shipfox/client-api": "4.0.0",
39
- "@shipfox/client-auth": "5.0.0",
40
- "@shipfox/client-integrations": "5.0.0",
41
- "@shipfox/client-shell": "5.0.0",
42
- "@shipfox/client-ui": "4.0.0",
43
- "@shipfox/react-ui": "0.3.5"
30
+ "@shipfox/api-definitions-dto": "9.0.1",
31
+ "@shipfox/api-projects-dto": "9.0.1",
32
+ "@shipfox/client-api": "6.0.1",
33
+ "@shipfox/client-agent": "6.0.1",
34
+ "@shipfox/api-integration-core-dto": "9.0.1",
35
+ "@shipfox/client-auth": "6.0.1",
36
+ "@shipfox/client-integrations": "6.0.1",
37
+ "@shipfox/client-shell": "6.0.1",
38
+ "@shipfox/client-ui": "6.0.1",
39
+ "@shipfox/react-ui": "0.3.6"
44
40
  },
45
41
  "peerDependencies": {
46
42
  "@tanstack/react-query": "^5.101.0",
@@ -49,6 +45,9 @@
49
45
  "react": "^19.0.0",
50
46
  "react-dom": "^19.0.0"
51
47
  },
48
+ "imports": {
49
+ "#*": "./dist/*"
50
+ },
52
51
  "scripts": {
53
52
  "build": "shipfox-swc",
54
53
  "check": "shipfox-biome-check",
package/src/chrome.tsx CHANGED
@@ -1,12 +1,17 @@
1
- import {useActiveWorkspace} from '@shipfox/client-shell/runtime';
2
- import {Outlet, useNavigate, useParams} from '@tanstack/react-router';
1
+ import {
2
+ parseWorkspaceProjectParams,
3
+ useActiveWorkspace,
4
+ useRouteParams,
5
+ } from '@shipfox/client-shell/runtime';
6
+ import {Outlet, useNavigate} from '@tanstack/react-router';
3
7
  import {useEffect} from 'react';
4
8
  import {ProjectCrumb} from '#components/project-crumb.js';
5
9
  import {useProjectQuery} from '#hooks/api/projects.js';
10
+ import {projectRouteParams} from './routes/inputs.js';
6
11
 
7
12
  export function ProjectBreadcrumb() {
8
13
  const workspace = useActiveWorkspace();
9
- const {pid} = useParams({strict: false}) as {pid?: string};
14
+ const {pid} = useRouteParams(parseWorkspaceProjectParams);
10
15
  const project = useProjectQuery(pid).data;
11
16
  return (
12
17
  <ProjectCrumb workspaceId={workspace.id} projectId={project?.id} projectName={project?.name} />
@@ -14,13 +19,13 @@ export function ProjectBreadcrumb() {
14
19
  }
15
20
 
16
21
  export function ProjectLayoutGuard() {
17
- const {wid, pid} = useParams({strict: false}) as {wid: string; pid: string};
22
+ const {wid, pid} = useRouteParams(projectRouteParams);
18
23
  const navigate = useNavigate();
19
24
  const project = useProjectQuery(pid).data;
20
25
  useEffect(() => {
21
- if (project && project.workspace_id !== wid)
26
+ if (project && project.workspaceId !== wid)
22
27
  void navigate({to: '/workspaces/$wid', params: {wid}, replace: true});
23
28
  }, [navigate, project, wid]);
24
- if (project && project.workspace_id !== wid) return null;
29
+ if (project && project.workspaceId !== wid) return null;
25
30
  return <Outlet />;
26
31
  }
@@ -17,9 +17,7 @@ export function ModelProviderReminderBanner({workspaceId}: {workspaceId: string}
17
17
  const [dismissed, setDismissed] = useState(() => isReminderDismissed(workspaceId));
18
18
  const configs = configsQuery.data?.configs;
19
19
  const unconfigured =
20
- Array.isArray(configs) &&
21
- configs.length === 0 &&
22
- configsQuery.data?.default_provider_id === null;
20
+ Array.isArray(configs) && configs.length === 0 && configsQuery.data?.defaultProviderId === null;
23
21
 
24
22
  if (!unconfigured || dismissed) return null;
25
23
 
@@ -8,6 +8,7 @@ import {
8
8
  RouterProvider,
9
9
  } from '@tanstack/react-router';
10
10
  import {fireEvent, render, screen} from '@testing-library/react';
11
+ import {toProject} from '#hooks/api/mappers.js';
11
12
  import {projectsQueryKeys} from '#hooks/api/projects.js';
12
13
  import {PROJECT_TEST_WID} from '#test/pages.js';
13
14
  import {ProjectSwitcher} from './project-switcher.js';
@@ -50,7 +51,7 @@ function renderProjectSwitcher({
50
51
  defaultOptions: {queries: {retry: false, staleTime: Number.POSITIVE_INFINITY}},
51
52
  });
52
53
  queryClient.setQueryData(projectsQueryKeys.list(PROJECT_TEST_WID), {
53
- pages: [{projects, next_cursor: null}],
54
+ pages: [{projects: projects.map(toProject), nextCursor: null}],
54
55
  pageParams: [undefined],
55
56
  });
56
57
  const rootRoute = createRootRoute({component: Outlet});
@@ -1,4 +1,3 @@
1
- import type {DefinitionSyncSummaryDto} from '@shipfox/api-definitions-dto';
2
1
  import {useActiveWorkspace} from '@shipfox/client-auth';
3
2
  import {useSourceConnectionsQuery} from '@shipfox/client-integrations';
4
3
  import {StatusBadge} from '@shipfox/react-ui/badge';
@@ -6,6 +5,7 @@ import {Icon, type IconName} from '@shipfox/react-ui/icon';
6
5
  import {Skeleton} from '@shipfox/react-ui/skeleton';
7
6
  import {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';
8
7
  import {Code, Text} from '@shipfox/react-ui/typography';
8
+ import type {DefinitionSyncSummary} from '#core/definition.js';
9
9
 
10
10
  /**
11
11
  * We cannot cheaply resolve `external_repository_id` → `owner/repo`
@@ -21,12 +21,12 @@ export function SourceStrip({
21
21
  }: {
22
22
  connectionId: string;
23
23
  externalRepositoryId: string;
24
- sync: DefinitionSyncSummaryDto | null | undefined;
24
+ sync: DefinitionSyncSummary | null | undefined;
25
25
  isPending: boolean;
26
26
  }) {
27
27
  const workspace = useActiveWorkspace();
28
28
  const connectionsQuery = useSourceConnectionsQuery(workspace.id);
29
- const connection = connectionsQuery.data?.connections.find((c) => c.id === connectionId);
29
+ const connection = connectionsQuery.data?.find((c) => c.id === connectionId);
30
30
 
31
31
  return (
32
32
  <section
@@ -40,7 +40,7 @@ export function SourceStrip({
40
40
  <Skeleton className="h-16 w-160" />
41
41
  ) : (
42
42
  <Text size="sm" bold className="truncate">
43
- {connection?.display_name ?? 'Connected source'}
43
+ {connection?.displayName ?? 'Connected source'}
44
44
  </Text>
45
45
  )}
46
46
  <Tooltip>
@@ -69,7 +69,7 @@ function SyncBadge({
69
69
  sync,
70
70
  isPending,
71
71
  }: {
72
- sync: DefinitionSyncSummaryDto | null | undefined;
72
+ sync: DefinitionSyncSummary | null | undefined;
73
73
  isPending: boolean;
74
74
  }) {
75
75
  if (isPending) return <StatusBadge variant="neutral">Loading</StatusBadge>;
@@ -0,0 +1,31 @@
1
+ export interface Definition {
2
+ id: string;
3
+ projectId: string;
4
+ configPath: string | null;
5
+ source: 'manual' | 'vcs';
6
+ sha: string | null;
7
+ ref: string | null;
8
+ name: string;
9
+ workflowDocument: unknown;
10
+ workflowModel: unknown;
11
+ manualTrigger: {name: string} | null;
12
+ fetchedAt: string;
13
+ createdAt: string;
14
+ updatedAt: string;
15
+ }
16
+
17
+ export interface DefinitionSyncSummary {
18
+ ref: string | null;
19
+ status: 'pending' | 'syncing' | 'succeeded' | 'failed';
20
+ lastSyncAt: string;
21
+ startedAt: string | null;
22
+ finishedAt: string | null;
23
+ lastErrorCode: string | null;
24
+ lastErrorMessage: string | null;
25
+ }
26
+
27
+ export interface DefinitionList {
28
+ definitions: Definition[];
29
+ sync: DefinitionSyncSummary | null;
30
+ nextCursor: string | null;
31
+ }
@@ -0,0 +1,47 @@
1
+ export interface ProjectSource {
2
+ connectionId: string;
3
+ externalRepositoryId: string;
4
+ }
5
+
6
+ export interface Project {
7
+ id: string;
8
+ workspaceId: string;
9
+ name: string;
10
+ source: ProjectSource;
11
+ createdAt: string;
12
+ updatedAt: string;
13
+ }
14
+
15
+ export interface ProjectList {
16
+ projects: Project[];
17
+ nextCursor: string | null;
18
+ }
19
+
20
+ export interface CreateProjectCommand {
21
+ workspaceId: string;
22
+ name: string;
23
+ source: ProjectSource;
24
+ }
25
+
26
+ const REPOSITORY_NAME_SPLIT_RE = /[/-]/;
27
+
28
+ export function projectNameFromRepository(repositoryId: string): string {
29
+ return repositoryId
30
+ .trim()
31
+ .split(REPOSITORY_NAME_SPLIT_RE)
32
+ .filter(Boolean)
33
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
34
+ .join(' ');
35
+ }
36
+
37
+ export function selectProjectSource<T extends {externalRepositoryId: string}>(
38
+ repositories: T[],
39
+ selectedRepositoryId: string | undefined,
40
+ ): T | undefined {
41
+ if (selectedRepositoryId) {
42
+ return repositories.find(
43
+ (repository) => repository.externalRepositoryId === selectedRepositoryId,
44
+ );
45
+ }
46
+ return repositories[0];
47
+ }
package/src/feature.ts CHANGED
@@ -1,4 +1,15 @@
1
- import {defineClientFeature} from '@shipfox/client-shell';
1
+ import {defineClientFeature, type NavTabEntry} from '@shipfox/client-shell';
2
+
3
+ export const projectsNavigation = [
4
+ {
5
+ id: 'nav.projects',
6
+ scope: 'workspace',
7
+ label: 'Projects',
8
+ to: '/workspaces/$wid',
9
+ exact: true,
10
+ order: 100,
11
+ },
12
+ ] as const satisfies readonly NavTabEntry[];
2
13
 
3
14
  export const projectsFeature = defineClientFeature({
4
15
  id: 'shipfox.projects',
@@ -19,14 +30,5 @@ export const projectsFeature = defineClientFeature({
19
30
  impl: '@shipfox/client-projects/routes/project-index',
20
31
  },
21
32
  ],
22
- navigation: [
23
- {
24
- id: 'nav.projects',
25
- scope: 'workspace',
26
- label: 'Projects',
27
- to: '/workspaces/$wid',
28
- exact: true,
29
- order: 100,
30
- },
31
- ],
33
+ navigation: projectsNavigation,
32
34
  });
@@ -1,12 +1,38 @@
1
- import type {DefinitionListResponseDto} from '@shipfox/api-definitions-dto';
2
- import {apiRequest} from '@shipfox/client-api';
3
- import {keepPreviousData, useInfiniteQuery, useQuery} from '@tanstack/react-query';
1
+ import {definitionListResponseSchema} from '@shipfox/api-definitions-dto';
2
+ import {checkedApiRequest} from '@shipfox/client-api';
3
+ import {
4
+ type InfiniteData,
5
+ keepPreviousData,
6
+ queryOptions,
7
+ type UseInfiniteQueryOptions,
8
+ type UseQueryOptions,
9
+ useInfiniteQuery,
10
+ useQuery,
11
+ } from '@tanstack/react-query';
12
+ import type {DefinitionList} from '#core/definition.js';
13
+ import {toDefinitionList} from './mappers.js';
4
14
 
5
15
  export const definitionsQueryKeys = {
6
16
  all: ['definitions'] as const,
7
17
  list: (projectId: string) => [...definitionsQueryKeys.all, 'list', projectId] as const,
8
18
  };
9
19
 
20
+ type DefinitionListQueryKey =
21
+ | ReturnType<typeof definitionsQueryKeys.list>
22
+ | readonly ['definitions', 'list'];
23
+ type DefinitionListInfiniteQueryOptions = UseInfiniteQueryOptions<
24
+ DefinitionList,
25
+ Error,
26
+ InfiniteData<DefinitionList, string | undefined>,
27
+ DefinitionListQueryKey,
28
+ string | undefined
29
+ >;
30
+ type DefinitionListQueryOptions = UseQueryOptions<
31
+ DefinitionList,
32
+ Error,
33
+ DefinitionList,
34
+ DefinitionListQueryKey
35
+ >;
10
36
  export async function listDefinitions({
11
37
  projectId,
12
38
  limit = 50,
@@ -16,35 +42,44 @@ export async function listDefinitions({
16
42
  projectId: string;
17
43
  limit?: number;
18
44
  cursor?: string | undefined;
19
- signal?: AbortSignal;
20
- }) {
45
+ signal?: AbortSignal | undefined;
46
+ }): Promise<DefinitionList> {
21
47
  const params = new URLSearchParams({project_id: projectId, limit: String(limit)});
22
48
  if (cursor) params.set('cursor', cursor);
23
- return await apiRequest<DefinitionListResponseDto>(`/definitions?${params.toString()}`, {
24
- signal,
25
- });
49
+ return toDefinitionList(
50
+ await checkedApiRequest(definitionListResponseSchema, `/definitions?${params.toString()}`, {
51
+ signal,
52
+ }),
53
+ );
26
54
  }
27
-
28
- export function useDefinitionsInfiniteQuery(projectId: string | undefined, limit = 50) {
29
- return useInfiniteQuery({
55
+ export function definitionsInfiniteQueryOptions(
56
+ projectId: string | undefined,
57
+ limit = 50,
58
+ ): DefinitionListInfiniteQueryOptions {
59
+ return {
30
60
  queryKey: projectId
31
61
  ? definitionsQueryKeys.list(projectId)
32
- : [...definitionsQueryKeys.all, 'list'],
62
+ : ([...definitionsQueryKeys.all, 'list'] as const),
33
63
  enabled: Boolean(projectId),
34
64
  initialPageParam: undefined as string | undefined,
35
- queryFn: ({pageParam, signal}) =>
65
+ queryFn: ({pageParam, signal}: {pageParam: string | undefined; signal: AbortSignal}) =>
36
66
  listDefinitions({projectId: projectId ?? '', limit, cursor: pageParam, signal}),
37
- getNextPageParam: (lastPage) => lastPage.next_cursor ?? undefined,
67
+ getNextPageParam: (lastPage: DefinitionList) => lastPage.nextCursor ?? undefined,
38
68
  placeholderData: keepPreviousData,
39
- });
69
+ };
40
70
  }
41
-
42
- export function useDefinitionsQuery(projectId: string | undefined) {
43
- return useQuery({
71
+ export function definitionsQueryOptions(projectId: string | undefined): DefinitionListQueryOptions {
72
+ return queryOptions({
44
73
  queryKey: projectId
45
74
  ? definitionsQueryKeys.list(projectId)
46
- : [...definitionsQueryKeys.all, 'list'],
75
+ : ([...definitionsQueryKeys.all, 'list'] as const),
47
76
  enabled: Boolean(projectId),
48
77
  queryFn: ({signal}) => listDefinitions({projectId: projectId ?? '', signal}),
49
78
  });
50
79
  }
80
+ export function useDefinitionsInfiniteQuery(projectId: string | undefined, limit = 50) {
81
+ return useInfiniteQuery(definitionsInfiniteQueryOptions(projectId, limit));
82
+ }
83
+ export function useDefinitionsQuery(projectId: string | undefined) {
84
+ return useQuery(definitionsQueryOptions(projectId));
85
+ }
@@ -0,0 +1,67 @@
1
+ import type {DefinitionDto, DefinitionSyncSummaryDto} from '@shipfox/api-definitions-dto';
2
+ import type {ProjectResponseDto} from '@shipfox/api-projects-dto';
3
+ import type {Definition, DefinitionList, DefinitionSyncSummary} from '#core/definition.js';
4
+ import type {Project, ProjectList} from '#core/project.js';
5
+
6
+ export function toProject(dto: ProjectResponseDto): Project {
7
+ return {
8
+ id: dto.id,
9
+ workspaceId: dto.workspace_id,
10
+ name: dto.name,
11
+ source: {
12
+ connectionId: dto.source.connection_id,
13
+ externalRepositoryId: dto.source.external_repository_id,
14
+ },
15
+ createdAt: dto.created_at,
16
+ updatedAt: dto.updated_at,
17
+ };
18
+ }
19
+
20
+ export function toProjectList(dto: {
21
+ projects: ProjectResponseDto[];
22
+ next_cursor: string | null;
23
+ }): ProjectList {
24
+ return {projects: dto.projects.map(toProject), nextCursor: dto.next_cursor};
25
+ }
26
+
27
+ export function toDefinition(dto: DefinitionDto): Definition {
28
+ return {
29
+ id: dto.id,
30
+ projectId: dto.project_id,
31
+ configPath: dto.config_path,
32
+ source: dto.source,
33
+ sha: dto.sha,
34
+ ref: dto.ref,
35
+ name: dto.name,
36
+ workflowDocument: dto.workflow_document,
37
+ workflowModel: dto.workflow_model,
38
+ manualTrigger: dto.manual_trigger,
39
+ fetchedAt: dto.fetched_at,
40
+ createdAt: dto.created_at,
41
+ updatedAt: dto.updated_at,
42
+ };
43
+ }
44
+
45
+ export function toDefinitionSyncSummary(dto: DefinitionSyncSummaryDto): DefinitionSyncSummary {
46
+ return {
47
+ ref: dto.ref,
48
+ status: dto.status,
49
+ lastSyncAt: dto.last_sync_at,
50
+ startedAt: dto.started_at,
51
+ finishedAt: dto.finished_at,
52
+ lastErrorCode: dto.last_error_code,
53
+ lastErrorMessage: dto.last_error_message,
54
+ };
55
+ }
56
+
57
+ export function toDefinitionList(dto: {
58
+ definitions: DefinitionDto[];
59
+ sync: DefinitionSyncSummaryDto | null;
60
+ next_cursor: string | null;
61
+ }): DefinitionList {
62
+ return {
63
+ definitions: dto.definitions.map(toDefinition),
64
+ sync: dto.sync && toDefinitionSyncSummary(dto.sync),
65
+ nextCursor: dto.next_cursor,
66
+ };
67
+ }
@@ -59,11 +59,11 @@ describe('createProject', () => {
59
59
  });
60
60
  configureApiClient({fetchImpl});
61
61
  const body = {
62
- workspace_id: '11111111-1111-4111-8111-111111111111',
62
+ workspaceId: '11111111-1111-4111-8111-111111111111',
63
63
  name: 'Platform',
64
64
  source: {
65
- connection_id: '33333333-3333-4333-8333-333333333333',
66
- external_repository_id: 'platform',
65
+ connectionId: '33333333-3333-4333-8333-333333333333',
66
+ externalRepositoryId: 'platform',
67
67
  },
68
68
  };
69
69
 
@@ -73,6 +73,13 @@ describe('createProject', () => {
73
73
  expect(result.name).toBe('Platform');
74
74
  expect(request.url).toBe('https://api.example.test/projects');
75
75
  expect(request.method).toBe('POST');
76
- expect(requestBody).toEqual(body);
76
+ expect(requestBody).toEqual({
77
+ workspace_id: body.workspaceId,
78
+ name: body.name,
79
+ source: {
80
+ connection_id: body.source.connectionId,
81
+ external_repository_id: body.source.externalRepositoryId,
82
+ },
83
+ });
77
84
  });
78
85
  });