@mapcreator/api 0.0.0-co-edit.4 → 0.0.0-co-edit.5

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 (55) hide show
  1. package/cjs/api/apiCommon.d.ts +3 -1
  2. package/cjs/api/apiCommon.d.ts.map +1 -1
  3. package/cjs/api/apiCommon.js +6 -13
  4. package/cjs/api/apiCommon.js.map +1 -1
  5. package/cjs/api/job.d.ts +22 -3
  6. package/cjs/api/job.d.ts.map +1 -1
  7. package/cjs/api/job.js +29 -4
  8. package/cjs/api/job.js.map +1 -1
  9. package/cjs/api/jobRevision.d.ts +2 -0
  10. package/cjs/api/jobRevision.d.ts.map +1 -1
  11. package/cjs/api/jobRevision.js +40 -1
  12. package/cjs/api/jobRevision.js.map +1 -1
  13. package/cjs/api/organisation.d.ts +2 -2
  14. package/cjs/api/organisation.d.ts.map +1 -1
  15. package/cjs/api/organisation.js +2 -3
  16. package/cjs/api/organisation.js.map +1 -1
  17. package/cjs/api/resources.d.ts +22 -0
  18. package/cjs/api/resources.d.ts.map +1 -1
  19. package/cjs/api/resources.js +2 -0
  20. package/cjs/api/resources.js.map +1 -1
  21. package/cjs/api/user.d.ts +5 -3
  22. package/cjs/api/user.d.ts.map +1 -1
  23. package/cjs/api/user.js +9 -5
  24. package/cjs/api/user.js.map +1 -1
  25. package/esm/api/apiCommon.d.ts +3 -1
  26. package/esm/api/apiCommon.d.ts.map +1 -1
  27. package/esm/api/apiCommon.js +6 -13
  28. package/esm/api/apiCommon.js.map +1 -1
  29. package/esm/api/job.d.ts +22 -3
  30. package/esm/api/job.d.ts.map +1 -1
  31. package/esm/api/job.js +23 -4
  32. package/esm/api/job.js.map +1 -1
  33. package/esm/api/jobRevision.d.ts +2 -0
  34. package/esm/api/jobRevision.d.ts.map +1 -1
  35. package/esm/api/jobRevision.js +40 -2
  36. package/esm/api/jobRevision.js.map +1 -1
  37. package/esm/api/organisation.d.ts +2 -2
  38. package/esm/api/organisation.d.ts.map +1 -1
  39. package/esm/api/organisation.js +2 -3
  40. package/esm/api/organisation.js.map +1 -1
  41. package/esm/api/resources.d.ts +22 -0
  42. package/esm/api/resources.d.ts.map +1 -1
  43. package/esm/api/resources.js +2 -0
  44. package/esm/api/resources.js.map +1 -1
  45. package/esm/api/user.d.ts +5 -3
  46. package/esm/api/user.d.ts.map +1 -1
  47. package/esm/api/user.js +7 -4
  48. package/esm/api/user.js.map +1 -1
  49. package/package.json +2 -1
  50. package/src/api/apiCommon.ts +21 -16
  51. package/src/api/job.ts +46 -6
  52. package/src/api/jobRevision.ts +59 -1
  53. package/src/api/organisation.ts +6 -6
  54. package/src/api/resources.ts +30 -0
  55. package/src/api/user.ts +21 -9
package/src/api/job.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { ApiJobShare, JobShare, JobShareVisibility } from './jobShare.js';
2
- import { type JobSearchResult, listJobs } from './apiCommon.js';
2
+ import { type DashboardSortType, type JobSearchResult, listJobs } from './apiCommon.js';
3
3
  import {
4
4
  type ApiCommon,
5
5
  type ApiCommonData,
@@ -23,6 +23,7 @@ export type Job = {
23
23
  previewPath: string | undefined;
24
24
  convertedJobId: number | null;
25
25
  private: boolean;
26
+ jobOpenUserName: string | null;
26
27
 
27
28
  userEmail: string | undefined; // ?
28
29
  orgName: string | undefined; // ?
@@ -42,6 +43,7 @@ export type ApiJob = {
42
43
  star: boolean; // TODO: not present in API specification!
43
44
  converted_job_id?: number | null;
44
45
  private: boolean;
46
+ job_open_user_name: string | null;
45
47
  } & ApiCommonData;
46
48
  } & Omit<ApiSuccess, 'data'> | ApiError;
47
49
 
@@ -58,9 +60,9 @@ export const jobRevivers: Revivers<ApiJob, Job> = {
58
60
  convertedJobId: (data: ApiJobData) => data.converted_job_id ?? null,
59
61
  };
60
62
 
61
- export async function createJob(title: string, oldJobId?: number): Promise<Job> {
63
+ export async function createJob(title: string, jobTypeId = 9, oldJobId?: number): Promise<Job> {
62
64
  const path = `/v1/jobs`;
63
- const body = { title, job_type_id: 9, ...oldJobId && { cloned_v4_job_id: oldJobId } };
65
+ const body = { title, job_type_id: jobTypeId, ...oldJobId && { cloned_v4_job_id: oldJobId } };
64
66
  const options = { revivers: jobRevivers };
65
67
 
66
68
  // Technically, the returning `data` will contain only the following fields:
@@ -146,9 +148,47 @@ export async function uploadJobPreview(
146
148
  }
147
149
 
148
150
  export async function listFeaturedJobs(
149
- title: string,
151
+ search: string,
152
+ sort: DashboardSortType,
150
153
  page: number,
151
- options?: Record<string, unknown>,
152
154
  ): Promise<JobSearchResult> {
153
- return listJobs(`/v1/jobs/featured`, title, page, options);
155
+ return listJobs(`/v1/jobs/featured`, search, sort, 'all', page);
156
+ }
157
+
158
+ export type JobCoEditEvent = {
159
+ jobId: number;
160
+ userName: string;
161
+ status: 'open' | 'close';
162
+ };
163
+
164
+ export type JobTakeoverEvent = {
165
+ userId: number;
166
+ userName: string;
167
+ requestedAt: string;
168
+ status: 'pending' | 'approved' | 'denied';
169
+ };
170
+
171
+ export async function openJob(jobId: number): Promise<Record<string, never>> {
172
+ return request<ApiCommon, Record<string, never>>(`/v1/jobs/${jobId}/open`, null, null, { method: 'POST' });
173
+ }
174
+
175
+ export async function heartbeat(jobId: number): Promise<Record<string, never>> {
176
+ // TODO handle disconnections. Throw the right error so we can show a modal in the tool when the connection is down
177
+ return request<ApiCommon, Record<string, never>>(`/v1/jobs/${jobId}/heartbeat`, null, null, { method: 'POST' });
178
+ }
179
+
180
+ export async function requestTakeover(jobId: number): Promise<Record<string, never>> {
181
+ return request<ApiCommon, Record<string, never>>(`/v1/jobs/${jobId}/takeover/request`, null, null, { method: 'POST' });
182
+ }
183
+
184
+ export async function approveTakeover(jobId: number, userId: number): Promise<Record<string, never>> {
185
+ return request<ApiCommon, Record<string, never>>(`/v1/jobs/${jobId}/takeover/approve`, { user_id: userId }, null, { method: 'POST' });
186
+ }
187
+
188
+ export async function denyTakeover(jobId: number, userId: number): Promise<Record<string, never>> {
189
+ return request<ApiCommon, Record<string, never>>(`/v1/jobs/${jobId}/takeover/deny`, { user_id: userId }, null, { method: 'POST' });
190
+ }
191
+
192
+ export async function closeJob(jobId: number): Promise<Record<string, never>> {
193
+ return request<ApiCommon, Record<string, never>>(`/v1/jobs/${jobId}/close`, null, null, { method: 'POST' });
154
194
  }
@@ -2,6 +2,7 @@ import { apiHost, authenticate, getAuthorizationHeaders, token } from '../oauth.
2
2
  import { type ApiLayerData, type Layer, layerRevivers } from './layer.js';
3
3
  import type { ApiMapstyleSetData } from './mapstyleSet.js';
4
4
  import type { ApiLanguageData } from './language.js';
5
+ import type Echo from 'laravel-echo';
5
6
  import {
6
7
  APIError,
7
8
  type ApiCommon,
@@ -13,6 +14,7 @@ import {
13
14
  NetworkError,
14
15
  type Revivers,
15
16
  TimeoutError,
17
+ combineSignals,
16
18
  defaultListHeader,
17
19
  defaultTimeout,
18
20
  deletedNoneParam,
@@ -225,11 +227,17 @@ export type JobRevisionOutput = { blob: Blob; filename?: string | undefined };
225
227
  export async function getJobRevisionOutput(jobId: number): Promise<JobRevisionOutput> {
226
228
  await createJobRevisionBuild(jobId);
227
229
 
230
+ return fetchOutput(jobId);
231
+ }
232
+
233
+ async function fetchOutput(jobId: number, signal?: AbortSignal): Promise<JobRevisionOutput> {
228
234
  const href = `${apiHost}/v1/jobs/${jobId}/revisions/${lastJobRevision}/result/output`;
229
235
  const headers = getAuthorizationHeaders();
230
236
 
231
237
  const timeout = defaultTimeout;
232
- const { signal, cleanup } = makeTimeoutSignal(timeout);
238
+ const { signal: timeoutSignal, cleanup } = makeTimeoutSignal(timeout);
239
+
240
+ signal = signal ? combineSignals(signal, timeoutSignal) : timeoutSignal;
233
241
 
234
242
  const response = await fetch(href, { headers, signal, ...!token && { credentials: 'include' } })
235
243
  .catch((error: Error) => {
@@ -296,3 +304,53 @@ async function createJobRevisionBuild(jobId: number, skipValidation?: boolean):
296
304
 
297
305
  return request<ApiCommon, string>(path, body, null, { method: 'POST' });
298
306
  }
307
+
308
+ async function waitForJobToFinish(jobId: number, echo: Echo<'reverb'>, signal?: AbortSignal): Promise<void> {
309
+ signal?.throwIfAborted();
310
+
311
+ return new Promise((resolve, reject) => {
312
+ const channel = `jobresult.${jobId}`;
313
+
314
+ const onAbort = (): void => {
315
+ echo.leaveChannel(channel);
316
+ reject(signal?.reason);
317
+ };
318
+
319
+ const cleanup = (): void => {
320
+ echo.leaveChannel(channel);
321
+ signal?.removeEventListener('abort', onAbort);
322
+ };
323
+
324
+ type JobResultEvent =
325
+ | { revisionId: number; status: string }
326
+ | { revisionId: number; filePathSet: boolean };
327
+
328
+ const handler = (event: JobResultEvent): void => {
329
+ if ('filePathSet' in event) {
330
+ cleanup();
331
+ resolve();
332
+ } else if (event.status !== 'processing' && event.status !== 'queued' && event.status !== 'completed') {
333
+ cleanup();
334
+ reject(new APIError({ success: false, error: { type: 'API Error', message: event.status } }));
335
+ }
336
+ };
337
+
338
+ signal?.addEventListener('abort', onAbort, { once: true });
339
+ echo.channel(channel).listen('.JobResultEvent', handler);
340
+ });
341
+ }
342
+
343
+ export async function getJobRevisionOutputForBackend(
344
+ jobId: number,
345
+ echo: Echo<'reverb'>,
346
+ signal?: AbortSignal,
347
+ ): Promise<JobRevisionOutput> {
348
+ await createJobRevisionBuild(jobId);
349
+ const jobrevision = await getJobRevision(jobId);
350
+
351
+ if (jobrevision) {
352
+ await waitForJobToFinish(jobrevision.id, echo, signal);
353
+ }
354
+
355
+ return fetchOutput(jobId);
356
+ }
@@ -1,5 +1,5 @@
1
1
  import type { ApiJobShareArray, JobShare } from './jobShare.js';
2
- import { type JobSearchResult, listJobs } from './apiCommon.js';
2
+ import { type DashboardFilterType, type DashboardSortType, type JobSearchResult, listJobs } from './apiCommon.js';
3
3
  import {
4
4
  type ApiCommonData,
5
5
  type ApiError,
@@ -74,13 +74,13 @@ export async function getOrganisation(
74
74
 
75
75
  export async function listOrganisationJobs(
76
76
  organisationId: number | typeof myOrganisations,
77
- title: string,
77
+ search: string,
78
+ sort: DashboardSortType,
79
+ filter: DashboardFilterType,
78
80
  page: number,
79
- options?: Record<string, unknown>,
81
+ folderId: number | null
80
82
  ): Promise<JobSearchResult> {
81
- const search = { ...options };
82
-
83
- return listJobs(`/v1/organisations/${organisationId}/jobs`, title, page, search);
83
+ return listJobs(`/v1/organisations/${organisationId}/jobs`, search, sort, filter, page, folderId);
84
84
  }
85
85
 
86
86
  export async function listOrganisationShares(jobId: number): Promise<JobShare[]> {
@@ -51,6 +51,30 @@ export type CustomFeature =
51
51
  | 'developer_menu'
52
52
  | 'allow_editing_other_users_jobs';
53
53
 
54
+ export type ApiAccessibleOrganisationFolder = {
55
+ id: number;
56
+ organisation_id: number;
57
+ name: string;
58
+ };
59
+
60
+ export type AccessibleOrganisationFolder = {
61
+ id: number;
62
+ organisationId: number;
63
+ name: string;
64
+ };
65
+
66
+ export type ApiAccessibleOrganisation = {
67
+ id: number;
68
+ name: string;
69
+ job_folders: ApiAccessibleOrganisationFolder[];
70
+ };
71
+
72
+ export type AccessibleOrganisation = {
73
+ id: number;
74
+ name: string;
75
+ jobFolders: AccessibleOrganisationFolder[];
76
+ };
77
+
54
78
  export interface Resources {
55
79
  organisation: Organisation;
56
80
  user: User;
@@ -66,6 +90,7 @@ export interface Resources {
66
90
  layers: Layer[];
67
91
  fonts: Font[];
68
92
  fontFamilies: FontFamily[];
93
+ accessibleOrganisations: AccessibleOrganisation[];
69
94
  }
70
95
 
71
96
  type ApiResources = {
@@ -88,6 +113,7 @@ type ApiResources = {
88
113
  layers: ApiLayerData[];
89
114
  fonts: ApiFontData[];
90
115
  font_families: ApiFontFamilyData[];
116
+ accessible_organisations: ApiAccessibleOrganisation[];
91
117
  } & ApiCommonData;
92
118
  } & Omit<ApiSuccess, 'data'> | ApiError;
93
119
 
@@ -132,6 +158,10 @@ export async function loadResources(): Promise<Resources> {
132
158
  svgSets: (raw.svgSets?.map(processData, getContext(svgSetRevivers)) ?? []) as SvgSet[],
133
159
  fonts: (raw.fonts?.map(processData, getContext(fontRevivers)) ?? []) as Font[],
134
160
  fontFamilies: (raw.fontFamilies?.map(processData, getContext(fontFamilyRevivers)) ?? []) as FontFamily[],
161
+ // TODO revivers and such
162
+ accessibleOrganisations: (raw.accessibleOrganisations?.map(
163
+ processData, getContext({})
164
+ ) ?? []) as AccessibleOrganisation[],
135
165
  };
136
166
  }
137
167
 
package/src/api/user.ts CHANGED
@@ -2,8 +2,9 @@ import { type ApiFontFamilyData, type FontFamily, fontFamilyRevivers } from './f
2
2
  import { type ApiSvgSetData, type SvgSet, svgSetRevivers } from './svgSet.js';
3
3
  import { type ApiLayerData, type Layer, layerRevivers } from './layer.js';
4
4
  import { type ApiSvgData, type Svg, svgRevivers } from './svg.js';
5
- import { type JobSearchResult, listJobs } from './apiCommon.js';
5
+ import { type DashboardFilterType, type DashboardSortType, type JobSearchResult, listJobs } from './apiCommon.js';
6
6
  import type { ApiJobShareArray, JobShare } from './jobShare.js';
7
+ import type { AccessibleOrganisation, ApiAccessibleOrganisation } from './resources.js';
7
8
  import type { RequireAtLeastOne } from 'type-fest';
8
9
  import {
9
10
  type ApiCommon,
@@ -210,21 +211,32 @@ export async function listUserHatchingSvgs(): Promise<Svg[]> {
210
211
 
211
212
  export async function listUserJobs(
212
213
  userId: number | typeof myUser,
213
- title: string,
214
+ search: string,
215
+ sort: DashboardSortType,
216
+ filter: DashboardFilterType,
214
217
  page: number,
215
- options?: Record<string, unknown>
216
218
  ): Promise<JobSearchResult> {
217
- return listJobs(`/v1/users/${userId}/jobs`, title, page, options);
219
+ return listJobs(`/v1/users/${userId}/jobs`, search, sort, filter, page);
218
220
  }
219
221
 
220
- export async function listUserFolders(): Promise<JobFolder[]> {
221
- const path = `/v1/users/${myUser}/folders`;
222
+ export async function listUserEditedJobs(
223
+ userId: number | typeof myUser,
224
+ search: string,
225
+ sort: DashboardSortType,
226
+ filter: DashboardFilterType,
227
+ page: number,
228
+ ): Promise<JobSearchResult> {
229
+ return listJobs(`/v1/users/${userId}/edited-jobs`, search, sort, filter, page);
230
+ }
231
+
232
+ export async function listUserAccessibleOrganisations(): Promise<AccessibleOrganisation[]> {
233
+ const path = `/v1/users/${myUser}/accessible-organisations`;
222
234
 
223
- type ApiJobFolderArray = {
224
- data: ApiJobFolderData[];
235
+ type ApiAccessibleOrganisationArray = {
236
+ data: ApiAccessibleOrganisation[];
225
237
  } & Omit<ApiSuccess, 'data'> | ApiError;
226
238
 
227
- return request<ApiJobFolderArray, JobFolder>(path);
239
+ return request<ApiAccessibleOrganisationArray, AccessibleOrganisation>(path);
228
240
  }
229
241
 
230
242
  export async function createUserFolder(name: string): Promise<JobFolder> {