@overmap-ai/core 1.0.48-bulk-form-submission.2 → 1.0.48-bulk-form-submission.4
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.
- package/dist/overmap-core.js +175 -35
- package/dist/overmap-core.js.map +1 -1
- package/dist/overmap-core.umd.cjs +175 -35
- package/dist/overmap-core.umd.cjs.map +1 -1
- package/dist/sdk/services/AttachmentService.d.ts +3 -2
- package/dist/sdk/services/UserFormSubmissionService.d.ts +2 -2
- package/dist/store/slices/formSubmissionSlice.d.ts +2 -1
- package/dist/store/slices/index.d.ts +1 -1
- package/dist/store/slices/issueSlice.d.ts +9 -1
- package/dist/store/slices/utils.d.ts +1 -0
- package/dist/typings/files.d.ts +11 -1
- package/dist/typings/models/attachments.d.ts +7 -10
- package/dist/typings/models/base.d.ts +5 -0
- package/dist/typings/models/components.d.ts +2 -3
- package/dist/typings/models/forms.d.ts +3 -5
- package/dist/utils/file.d.ts +2 -0
- package/package.json +1 -1
package/dist/overmap-core.js
CHANGED
|
@@ -804,6 +804,19 @@ function downloadInMemoryFile(filename, text) {
|
|
|
804
804
|
element.click();
|
|
805
805
|
document.body.removeChild(element);
|
|
806
806
|
}
|
|
807
|
+
const constructUploadedFilePayloads = async (files) => {
|
|
808
|
+
const filePayloads = {};
|
|
809
|
+
for (const file of files) {
|
|
810
|
+
const sha1 = await hashFile(file);
|
|
811
|
+
filePayloads[sha1] = {
|
|
812
|
+
sha1,
|
|
813
|
+
extension: file.name.split(".").pop() || "",
|
|
814
|
+
file_type: file.type,
|
|
815
|
+
size: file.size
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
return Object.values(filePayloads);
|
|
819
|
+
};
|
|
807
820
|
const fileToBlob = async (dataUrl) => {
|
|
808
821
|
return (await fetch(dataUrl)).blob();
|
|
809
822
|
};
|
|
@@ -1537,6 +1550,15 @@ function updateAttachment(state, action) {
|
|
|
1537
1550
|
throw new Error(`Attachment ${action.payload.offline_id} does not exist.`);
|
|
1538
1551
|
}
|
|
1539
1552
|
}
|
|
1553
|
+
function updateAttachments(state, action) {
|
|
1554
|
+
for (const attachment of action.payload) {
|
|
1555
|
+
if (attachment.offline_id in state.attachments) {
|
|
1556
|
+
state.attachments[attachment.offline_id] = attachment;
|
|
1557
|
+
} else {
|
|
1558
|
+
throw new Error(`Attachment ${attachment.offline_id} does not exist.`);
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1540
1562
|
function removeAttachment(state, action) {
|
|
1541
1563
|
if (action.payload in state.attachments) {
|
|
1542
1564
|
delete state.attachments[action.payload];
|
|
@@ -2130,6 +2152,7 @@ const issueSlice = createSlice({
|
|
|
2130
2152
|
}
|
|
2131
2153
|
},
|
|
2132
2154
|
updateIssueAttachment: updateAttachment,
|
|
2155
|
+
updateIssueAttachments: updateAttachments,
|
|
2133
2156
|
removeIssue: (state, action) => {
|
|
2134
2157
|
if (action.payload in state.issues) {
|
|
2135
2158
|
delete state.issues[action.payload];
|
|
@@ -2138,6 +2161,7 @@ const issueSlice = createSlice({
|
|
|
2138
2161
|
}
|
|
2139
2162
|
},
|
|
2140
2163
|
removeIssueAttachment: removeAttachment,
|
|
2164
|
+
removeIssueAttachments: removeAttachments,
|
|
2141
2165
|
removeIssueUpdate: (state, action) => {
|
|
2142
2166
|
if (action.payload in state.updates) {
|
|
2143
2167
|
delete state.updates[action.payload];
|
|
@@ -2247,6 +2271,7 @@ const {
|
|
|
2247
2271
|
addToRecentIssues,
|
|
2248
2272
|
cleanRecentIssues,
|
|
2249
2273
|
removeIssueAttachment,
|
|
2274
|
+
removeIssueAttachments,
|
|
2250
2275
|
removeAttachmentsOfIssue,
|
|
2251
2276
|
removeIssue,
|
|
2252
2277
|
removeIssueUpdate,
|
|
@@ -2260,6 +2285,7 @@ const {
|
|
|
2260
2285
|
setVisibleStatuses,
|
|
2261
2286
|
setVisibleUserIds,
|
|
2262
2287
|
updateIssueAttachment,
|
|
2288
|
+
updateIssueAttachments,
|
|
2263
2289
|
updateIssue,
|
|
2264
2290
|
// Commments
|
|
2265
2291
|
addIssueComment,
|
|
@@ -3823,6 +3849,16 @@ const formSubmissionSlice = createSlice({
|
|
|
3823
3849
|
state.attachments[attachment.offline_id] = attachment;
|
|
3824
3850
|
}
|
|
3825
3851
|
},
|
|
3852
|
+
updateFormSubmissionAttachments: (state, action) => {
|
|
3853
|
+
for (const attachment of action.payload) {
|
|
3854
|
+
if (state.attachments[attachment.offline_id] === void 0) {
|
|
3855
|
+
throw new Error(`Attachment with offline_id ${attachment.offline_id} does not exist`);
|
|
3856
|
+
}
|
|
3857
|
+
}
|
|
3858
|
+
for (const attachment of action.payload) {
|
|
3859
|
+
state.attachments[attachment.offline_id] = attachment;
|
|
3860
|
+
}
|
|
3861
|
+
},
|
|
3826
3862
|
// The delete actions for UserFormSubmissionAttachments are not used in the app, but are included for completeness
|
|
3827
3863
|
// Could be used if editing a submission is ever supported, will be applicable for supporting tip tap content in submissions
|
|
3828
3864
|
deleteFormSubmissionAttachment: (state, action) => {
|
|
@@ -3853,6 +3889,7 @@ const {
|
|
|
3853
3889
|
addFormSubmissionAttachment,
|
|
3854
3890
|
addFormSubmissionAttachments,
|
|
3855
3891
|
setFormSubmissionAttachments,
|
|
3892
|
+
updateFormSubmissionAttachments,
|
|
3856
3893
|
deleteFormSubmissionAttachment,
|
|
3857
3894
|
deleteFormSubmissionAttachments
|
|
3858
3895
|
} = formSubmissionSlice.actions;
|
|
@@ -4706,6 +4743,22 @@ class BaseApiService {
|
|
|
4706
4743
|
}
|
|
4707
4744
|
}
|
|
4708
4745
|
class AttachmentService extends BaseApiService {
|
|
4746
|
+
processPresignedUrls(presignedUrls) {
|
|
4747
|
+
for (const [sha1, presignedUrl] of Object.entries(presignedUrls)) {
|
|
4748
|
+
void this.enqueueRequest({
|
|
4749
|
+
url: presignedUrl.url,
|
|
4750
|
+
description: "Upload file",
|
|
4751
|
+
method: HttpMethod.POST,
|
|
4752
|
+
isExternalUrl: true,
|
|
4753
|
+
isAuthNeeded: false,
|
|
4754
|
+
attachmentHash: sha1,
|
|
4755
|
+
// TODO: can we use the sha1 as the blocker?
|
|
4756
|
+
blockers: [`s3-${sha1}`],
|
|
4757
|
+
blocks: [sha1],
|
|
4758
|
+
s3url: presignedUrl
|
|
4759
|
+
});
|
|
4760
|
+
}
|
|
4761
|
+
}
|
|
4709
4762
|
fetchAll(projectId) {
|
|
4710
4763
|
const promise = this.enqueueRequest({
|
|
4711
4764
|
description: "Fetch attachments",
|
|
@@ -4731,6 +4784,7 @@ class AttachmentService extends BaseApiService {
|
|
|
4731
4784
|
}
|
|
4732
4785
|
const offlineAttachment = {
|
|
4733
4786
|
...attachmentPayload,
|
|
4787
|
+
// TODO: just handle creating the objectURL in here, then the front end doesn't need to worry about it
|
|
4734
4788
|
file: attachmentPayload.file.objectURL,
|
|
4735
4789
|
file_name: attachmentPayload.file.name,
|
|
4736
4790
|
file_type: attachmentPayload.file.type,
|
|
@@ -4764,6 +4818,7 @@ class AttachmentService extends BaseApiService {
|
|
|
4764
4818
|
}
|
|
4765
4819
|
const offlineAttachment = {
|
|
4766
4820
|
...attachmentPayload,
|
|
4821
|
+
// TODO: just handle creating the objectURL in here, then the front end doesn't need to worry about it
|
|
4767
4822
|
file: attachmentPayload.file.objectURL,
|
|
4768
4823
|
file_name: attachmentPayload.file.name,
|
|
4769
4824
|
file_type: attachmentPayload.file.type,
|
|
@@ -4797,6 +4852,7 @@ class AttachmentService extends BaseApiService {
|
|
|
4797
4852
|
}
|
|
4798
4853
|
const offlineAttachment = {
|
|
4799
4854
|
...attachmentPayload,
|
|
4855
|
+
// TODO: just handle creating the objectURL in here, then the front end doesn't need to worry about it
|
|
4800
4856
|
file: attachmentPayload.file.objectURL,
|
|
4801
4857
|
file_name: attachmentPayload.file.name,
|
|
4802
4858
|
file_type: attachmentPayload.file.type,
|
|
@@ -4849,7 +4905,7 @@ class AttachmentService extends BaseApiService {
|
|
|
4849
4905
|
offline_id,
|
|
4850
4906
|
project,
|
|
4851
4907
|
description: description2 ?? "",
|
|
4852
|
-
submitted_at: (/* @__PURE__ */ new Date()).
|
|
4908
|
+
submitted_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4853
4909
|
...fileProps
|
|
4854
4910
|
}
|
|
4855
4911
|
});
|
|
@@ -4862,26 +4918,54 @@ class AttachmentService extends BaseApiService {
|
|
|
4862
4918
|
/** the outer Promise is needed to await the hashing of each file, which is required before offline use. If wanting to
|
|
4863
4919
|
* attach promise handlers to the request to add the attachment in the backend, apply it on the promise returned from the
|
|
4864
4920
|
* OptimisticModelResult. */
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
};
|
|
4883
|
-
|
|
4921
|
+
// note the method is only marked as async since files needs to be hashed
|
|
4922
|
+
async attachFilesToIssue(files, issueId) {
|
|
4923
|
+
const { store } = this.client;
|
|
4924
|
+
const offlineAttachments = [];
|
|
4925
|
+
const attachmentsPayload = [];
|
|
4926
|
+
const currentUser = store.getState().userReducer.currentUser;
|
|
4927
|
+
const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4928
|
+
for (const file of files) {
|
|
4929
|
+
const attachment = offline({
|
|
4930
|
+
file: URL.createObjectURL(file),
|
|
4931
|
+
file_name: file.name,
|
|
4932
|
+
file_type: file.type,
|
|
4933
|
+
file_sha1: await hashFile(file),
|
|
4934
|
+
description: "",
|
|
4935
|
+
submitted_at: submittedAt,
|
|
4936
|
+
created_by: currentUser.id,
|
|
4937
|
+
issue: issueId
|
|
4938
|
+
});
|
|
4939
|
+
attachmentsPayload.push({
|
|
4940
|
+
offline_id: attachment.offline_id,
|
|
4941
|
+
name: attachment.file_name,
|
|
4942
|
+
sha1: attachment.file_sha1,
|
|
4943
|
+
description: attachment.description,
|
|
4944
|
+
created_by: attachment.created_by,
|
|
4945
|
+
issue_id: attachment.issue
|
|
4946
|
+
});
|
|
4947
|
+
offlineAttachments.push(attachment);
|
|
4948
|
+
}
|
|
4949
|
+
store.dispatch(addIssueAttachments(offlineAttachments));
|
|
4950
|
+
const promise = this.enqueueRequest({
|
|
4951
|
+
description: "Attach files to issue",
|
|
4952
|
+
method: HttpMethod.POST,
|
|
4953
|
+
url: `/issues/${issueId}/attach/`,
|
|
4954
|
+
payload: {
|
|
4955
|
+
submitted_at: submittedAt,
|
|
4956
|
+
attachments: attachmentsPayload,
|
|
4957
|
+
files: await constructUploadedFilePayloads(files)
|
|
4958
|
+
},
|
|
4959
|
+
blocks: offlineAttachments.map((attachment) => attachment.offline_id),
|
|
4960
|
+
blockers: offlineAttachments.map((attachment) => attachment.file_sha1)
|
|
4961
|
+
});
|
|
4962
|
+
promise.then(({ attachments, presigned_urls }) => {
|
|
4963
|
+
store.dispatch(updateIssueAttachments(attachments));
|
|
4964
|
+
this.processPresignedUrls(presigned_urls);
|
|
4965
|
+
}).catch(() => {
|
|
4966
|
+
store.dispatch(removeIssueAttachments(offlineAttachments.map((attachment) => attachment.offline_id)));
|
|
4884
4967
|
});
|
|
4968
|
+
return [offlineAttachments, promise.then(({ attachments }) => attachments)];
|
|
4885
4969
|
}
|
|
4886
4970
|
attachFilesToComponent(filesToSubmit, componentId) {
|
|
4887
4971
|
return filesToSubmit.map((file) => {
|
|
@@ -7157,17 +7241,18 @@ class UserFormSubmissionService extends BaseApiService {
|
|
|
7157
7241
|
}
|
|
7158
7242
|
// Note currently the bulkAdd method is specific to form submissions for components
|
|
7159
7243
|
// TODO: adapt the support bulk adding to any model type
|
|
7160
|
-
bulkAdd(args) {
|
|
7161
|
-
const { form_revision, values: argsValues,
|
|
7244
|
+
async bulkAdd(args) {
|
|
7245
|
+
const { form_revision, values: argsValues, componentOfflineIds } = args;
|
|
7162
7246
|
const { store } = this.client;
|
|
7163
|
-
const
|
|
7247
|
+
const offlineSubmissions = [];
|
|
7248
|
+
const offlineAttachments = [];
|
|
7164
7249
|
const submissionOfflineIds = [];
|
|
7165
|
-
const
|
|
7166
|
-
|
|
7250
|
+
const submissionsPayload = [];
|
|
7251
|
+
const attachmentsPayload = [];
|
|
7167
7252
|
const { values, files } = separateFilesFromValues(argsValues);
|
|
7168
7253
|
const submittedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7169
7254
|
const createdBy = store.getState().userReducer.currentUser.id;
|
|
7170
|
-
for (const component_id of
|
|
7255
|
+
for (const component_id of componentOfflineIds) {
|
|
7171
7256
|
const submission = offline({
|
|
7172
7257
|
form_revision,
|
|
7173
7258
|
values,
|
|
@@ -7175,12 +7260,43 @@ class UserFormSubmissionService extends BaseApiService {
|
|
|
7175
7260
|
submitted_at: submittedAt,
|
|
7176
7261
|
component: component_id
|
|
7177
7262
|
});
|
|
7178
|
-
attachFilesPromises = attachFilesPromises.concat(this.getAttachFilesPromises(files, submission));
|
|
7179
7263
|
submissionOfflineIds.push(submission.offline_id);
|
|
7180
|
-
|
|
7181
|
-
|
|
7264
|
+
submissionsPayload.push({ offline_id: submission.offline_id, component_id });
|
|
7265
|
+
offlineSubmissions.push(submission);
|
|
7266
|
+
for (const [fieldIdentifier, fileArray] of Object.entries(files)) {
|
|
7267
|
+
for (const file of fileArray) {
|
|
7268
|
+
const sha1 = await hashFile(file);
|
|
7269
|
+
await this.client.files.addCache(file, sha1);
|
|
7270
|
+
const offlineAttachment = offline({
|
|
7271
|
+
file_name: file.name,
|
|
7272
|
+
file_sha1: sha1,
|
|
7273
|
+
file: URL.createObjectURL(file),
|
|
7274
|
+
submission: submission.offline_id,
|
|
7275
|
+
field_identifier: fieldIdentifier
|
|
7276
|
+
});
|
|
7277
|
+
offlineAttachments.push(offlineAttachment);
|
|
7278
|
+
attachmentsPayload.push({
|
|
7279
|
+
offline_id: offlineAttachment.offline_id,
|
|
7280
|
+
submission_id: submission.offline_id,
|
|
7281
|
+
sha1,
|
|
7282
|
+
name: file.name,
|
|
7283
|
+
field_identifier: fieldIdentifier
|
|
7284
|
+
});
|
|
7285
|
+
}
|
|
7286
|
+
}
|
|
7287
|
+
}
|
|
7288
|
+
const filesRecord = {};
|
|
7289
|
+
for (const file of Object.values(files).flat()) {
|
|
7290
|
+
const sha1 = await hashFile(file);
|
|
7291
|
+
filesRecord[sha1] = {
|
|
7292
|
+
sha1,
|
|
7293
|
+
extension: file.name.split(".").pop() || "",
|
|
7294
|
+
file_type: file.type,
|
|
7295
|
+
size: file.size
|
|
7296
|
+
};
|
|
7182
7297
|
}
|
|
7183
|
-
store.dispatch(addFormSubmissions(
|
|
7298
|
+
store.dispatch(addFormSubmissions(offlineSubmissions));
|
|
7299
|
+
store.dispatch(addFormSubmissionAttachments(offlineAttachments));
|
|
7184
7300
|
const promise = this.enqueueRequest({
|
|
7185
7301
|
description: "Bulk add form submissions",
|
|
7186
7302
|
method: HttpMethod.POST,
|
|
@@ -7188,17 +7304,37 @@ class UserFormSubmissionService extends BaseApiService {
|
|
|
7188
7304
|
payload: {
|
|
7189
7305
|
form_data: values,
|
|
7190
7306
|
submitted_at: submittedAt,
|
|
7191
|
-
|
|
7307
|
+
submissions: submissionsPayload,
|
|
7308
|
+
attachments: attachmentsPayload,
|
|
7309
|
+
files: Object.values(filesRecord)
|
|
7192
7310
|
},
|
|
7193
|
-
blockers:
|
|
7311
|
+
blockers: componentOfflineIds,
|
|
7194
7312
|
blocks: submissionOfflineIds
|
|
7195
7313
|
});
|
|
7196
|
-
promise.then((
|
|
7197
|
-
store.dispatch(updateFormSubmissions(
|
|
7314
|
+
promise.then(({ submissions, attachments, presigned_urls }) => {
|
|
7315
|
+
store.dispatch(updateFormSubmissions(submissions));
|
|
7316
|
+
store.dispatch(updateFormSubmissionAttachments(attachments));
|
|
7317
|
+
for (const [sha1, presigned_url] of Object.entries(presigned_urls)) {
|
|
7318
|
+
const file = filesRecord[sha1];
|
|
7319
|
+
if (!file)
|
|
7320
|
+
continue;
|
|
7321
|
+
void this.enqueueRequest({
|
|
7322
|
+
url: presigned_url.url,
|
|
7323
|
+
description: "Upload file",
|
|
7324
|
+
method: HttpMethod.POST,
|
|
7325
|
+
isExternalUrl: true,
|
|
7326
|
+
isAuthNeeded: false,
|
|
7327
|
+
attachmentHash: sha1,
|
|
7328
|
+
blockers: [`s3-${file.sha1}.${file.extension}`],
|
|
7329
|
+
blocks: [sha1],
|
|
7330
|
+
s3url: presigned_url
|
|
7331
|
+
});
|
|
7332
|
+
}
|
|
7198
7333
|
}).catch(() => {
|
|
7199
7334
|
store.dispatch(deleteFormSubmissions(submissionOfflineIds));
|
|
7335
|
+
store.dispatch(deleteFormSubmissionAttachments(offlineAttachments.map((x) => x.offline_id)));
|
|
7200
7336
|
});
|
|
7201
|
-
return [
|
|
7337
|
+
return [offlineSubmissions, promise.then(({ submissions }) => submissions)];
|
|
7202
7338
|
}
|
|
7203
7339
|
update(submission) {
|
|
7204
7340
|
const { store } = this.client;
|
|
@@ -15831,6 +15967,7 @@ export {
|
|
|
15831
15967
|
componentStageSlice,
|
|
15832
15968
|
componentTypeReducer,
|
|
15833
15969
|
componentTypeSlice,
|
|
15970
|
+
constructUploadedFilePayloads,
|
|
15834
15971
|
coordinatesAreEqual,
|
|
15835
15972
|
coordinatesToLiteral,
|
|
15836
15973
|
coordinatesToPointGeometry,
|
|
@@ -15959,6 +16096,7 @@ export {
|
|
|
15959
16096
|
removeFavouriteProjectId,
|
|
15960
16097
|
removeIssue,
|
|
15961
16098
|
removeIssueAttachment,
|
|
16099
|
+
removeIssueAttachments,
|
|
15962
16100
|
removeIssueComment,
|
|
15963
16101
|
removeIssueComments,
|
|
15964
16102
|
removeIssueUpdate,
|
|
@@ -16223,9 +16361,11 @@ export {
|
|
|
16223
16361
|
updateComponentTypeAttachment,
|
|
16224
16362
|
updateDocuments,
|
|
16225
16363
|
updateFormSubmission,
|
|
16364
|
+
updateFormSubmissionAttachments,
|
|
16226
16365
|
updateFormSubmissions,
|
|
16227
16366
|
updateIssue,
|
|
16228
16367
|
updateIssueAttachment,
|
|
16368
|
+
updateIssueAttachments,
|
|
16229
16369
|
updateLicense,
|
|
16230
16370
|
updateOrCreateProject,
|
|
16231
16371
|
updateOrganizationAccess,
|