Skip to content

Commit

Permalink
Moved API calls from formio to FormViewer (bcgov#1396)
Browse files Browse the repository at this point in the history
* Moved API calls from formio to FormViewer

API calls for file uploads have been moved from formio to the FormViewer.

* Update Component.ts

this should fix the issue of a missing storage for the file upload component. we don't actually use a storage but it's required to hide the message about a missing storage.
  • Loading branch information
jasonchung1871 authored Jul 24, 2024
1 parent f1bb9e3 commit f0fa814
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 221 deletions.
59 changes: 56 additions & 3 deletions app/frontend/src/components/designer/FormViewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@ import BaseDialog from '~/components/base/BaseDialog.vue';
import FormViewerActions from '~/components/designer/FormViewerActions.vue';
import FormViewerMultiUpload from '~/components/designer/FormViewerMultiUpload.vue';
import templateExtensions from '~/plugins/templateExtensions';
import { formService, rbacService } from '~/services';
import { fileService, formService, rbacService } from '~/services';
import { useAppStore } from '~/store/app';
import { useAuthStore } from '~/store/auth';
import { useFormStore } from '~/store/form';
import { useNotificationStore } from '~/store/notification';
import { isFormPublic } from '~/utils/permissionUtils';
import { attachAttributesToLinks } from '~/utils/transformUtils';
import {
attachAttributesToLinks,
getDisposition,
} from '~/utils/transformUtils';
import { FormPermissions, NotificationTypes } from '~/utils/constants';
export default {
Expand Down Expand Up @@ -77,6 +80,7 @@ export default {
bulkFile: false,
confirmSubmit: false,
currentForm: {},
downloadTimeout: null,
doYouWantToSaveTheDraft: false,
forceNewTabLinks: true,
form: {},
Expand Down Expand Up @@ -121,7 +125,7 @@ export default {
'tokenParsed',
'user',
]),
...mapState(useFormStore, ['isRTL']),
...mapState(useFormStore, ['downloadedFile', 'isRTL']),
formScheduleExpireMessage() {
return this.$t('trans.formViewer.formScheduleExpireMessage');
Expand All @@ -148,6 +152,9 @@ export default {
simplefile: {
config: this.config,
chefsToken: this.getCurrentAuthHeader,
deleteFile: this.deleteFile,
getFile: this.getFile,
uploadFile: this.uploadFile,
},
},
evalContext: {
Expand Down Expand Up @@ -190,13 +197,15 @@ export default {
},
beforeUnmount() {
window.removeEventListener('beforeunload', this.beforeWindowUnload);
clearTimeout(this.downloadTimeout);
},
beforeUpdate() {
if (this.forceNewTabLinks) {
attachAttributesToLinks(this.formSchema.components);
}
},
methods: {
...mapActions(useFormStore, ['downloadFile']),
...mapActions(useNotificationStore, ['addNotification']),
isFormPublic: isFormPublic,
getCurrentAuthHeader() {
Expand Down Expand Up @@ -1079,6 +1088,50 @@ export default {
e.returnValue = '';
}
},
async deleteFile(file) {
return fileService.deleteFile(file.id);
},
async getFile(fileId, options = {}) {
await this.downloadFile(fileId, options);
if (this.downloadedFile && this.downloadedFile.headers) {
let data;
if (
this.downloadedFile.headers['content-type'].includes(
'application/json'
)
) {
data = JSON.stringify(this.downloadedFile.data);
} else {
data = this.downloadedFile.data;
}
if (typeof data === 'string') {
data = new Blob([data], {
type: this.downloadedFile.headers['content-type'],
});
}
// don't need to blob because it's already a blob
const url = window.URL.createObjectURL(data);
const a = document.createElement('a');
a.href = url;
a.download = getDisposition(
this.downloadedFile.headers['content-disposition']
);
a.style.display = 'none';
a.classList.add('hiddenDownloadTextElement');
document.body.appendChild(a);
a.click();
this.downloadTimeout = setTimeout(() => {
document.body.removeChild(a);
URL.revokeObjectURL(a.href);
});
}
},
async uploadFile(file, config = {}) {
return fileService.uploadFile(file, config);
},
},
};
</script>
Expand Down
10 changes: 8 additions & 2 deletions app/frontend/src/services/fileService.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ import { appAxios } from '~/services/interceptors';
import { ApiRoutes } from '~/utils/constants';

export default {
async getFile(fileId) {
return appAxios().get(`${ApiRoutes.FILES}/${fileId}`);
async deleteFile(fileId) {
return appAxios().delete(`${ApiRoutes.FILES}/${fileId}`);
},
async getFile(fileId, options = {}) {
return appAxios().get(`${ApiRoutes.FILES}/${fileId}`, options);
},
async uploadFile(file, config = {}) {
return appAxios().post(`${ApiRoutes.FILES}`, file, config);
},
};
4 changes: 2 additions & 2 deletions app/frontend/src/store/form.js
Original file line number Diff line number Diff line change
Expand Up @@ -826,10 +826,10 @@ export const useFormStore = defineStore('form', {
if (!this.form || this.form.isDirty === isDirty) return; // don't do anything if not changing the val (or if form is blank for some reason)
this.form.isDirty = isDirty;
},
async downloadFile(fileId) {
async downloadFile(fileId, options = {}) {
try {
this.downloadedFile = {};
const response = await fileService.getFile(fileId);
const response = await fileService.getFile(fileId, options);
this.downloadedFile.data = response.data;
this.downloadedFile.headers = response.headers;
} catch (error) {
Expand Down
70 changes: 46 additions & 24 deletions components/src/components/SimpleFile/Component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,8 @@ export default class Component extends (ParentComponent as any) {

deleteFile(fileInfo) {
const { options = {} } = this.component;
const Provider = Formio.Providers.getProvider('storage', this.component.storage);
if (Provider) {
const provider = new Provider(this);
if (fileInfo && provider && typeof provider.deleteFile === 'function') {
provider.deleteFile(fileInfo, options)
}
if (fileInfo) {
options.deleteFile(fileInfo);
}
}

Expand All @@ -89,9 +85,9 @@ export default class Component extends (ParentComponent as any) {
if (!this.component.multiple) {
files = Array.prototype.slice.call(files, 0, 1);
}
if (this.component.storage && files && files.length) {
if (this.component && files && files.length) {
// files is not really an array and does not have a forEach method, so fake it.
Array.prototype.forEach.call(files, (file) => {
Array.prototype.forEach.call(files, async (file) => {
const fileName = uniqueName(file.name, this.component.fileNameTemplate, this.evalContext());
const fileUpload = {
originalName: file.name,
Expand Down Expand Up @@ -140,7 +136,7 @@ export default class Component extends (ParentComponent as any) {
if (this.component.privateDownload) {
file.private = true;
}
const { storage, options = {} } = this.component;
const { options = {} } = this.component;
const url = this.interpolate(this.component.url);
let groupKey = null;
let groupPermissions = null;
Expand All @@ -162,19 +158,48 @@ export default class Component extends (ParentComponent as any) {
});

const fileKey = this.component.fileKey || 'file';
const groupResourceId = groupKey ? this.currentForm.submission.data[groupKey]._id : null;
fileService.uploadFile(storage, file, fileName, dir, (evt) => {
fileUpload.status = 'progress';
// @ts-ignore
fileUpload.progress = parseInt(100.0 * evt.loaded / evt.total);
delete fileUpload.message;
this.redraw();
}, url, options, fileKey, groupPermissions, groupResourceId)
.then((fileInfo) => {

const blob = new Blob([file], { type: file.type });
const fileFromBlob = new File([blob], file.name, {
type: file.type,
lastModified: file.lastModified,
});
const formData = new FormData();
const data = {
[fileKey]: fileFromBlob,
fileName,
dir,
};
for (const key in data) {
formData.append(key, data[key]);
}
options.uploadFile(formData, {
onUploadProgress: (evt) => {
fileUpload.status = 'progress';
// @ts-ignore
fileUpload.progress = parseInt(100.0 * evt.loaded / evt.total);
delete fileUpload.message;
this.redraw();
},
headers: {
'Content-Type': 'multipart/form-data',
},
})
.then((response) => {
response.data = response.data || {};
const index = this.statuses.indexOf(fileUpload);
if (index !== -1) {
this.statuses.splice(index, 1);
}
let fileInfo = {
storage: 'chefs',
name: response.data.originalname,
originalName: '',
url: `${url}/${response.data.id}`,
size: response.data.size,
type: response.data.mimetype,
data: { id: response.data.id },
};
fileInfo.originalName = file.name;
if (!this.hasValue()) {
this.dataValue = [];
Expand All @@ -190,19 +215,16 @@ export default class Component extends (ParentComponent as any) {
// @ts-ignore
delete fileUpload.progress;
this.redraw();
});
})
}
});
}
}

getFile(fileInfo) {
const fileId = fileInfo?.data?.id ? fileInfo.data.id : fileInfo.id;
const { options = {} } = this.component;
const { fileService } = this;
if (!fileService) {
return alert('File Service not provided');
}
fileService.downloadFile(fileInfo, options)
options.getFile(fileId, { responseType: 'blob' })
.catch((response) => {
// Is alert the best way to do this?
// User is expecting an immediate notification due to attempting to download a file.
Expand Down
3 changes: 0 additions & 3 deletions components/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import './overrides/editform/utils';

import components from './components';
// @ts-ignore
import providers from './providers';

export default {
components,
providers,
};
5 changes: 0 additions & 5 deletions components/src/providers/index.ts

This file was deleted.

Loading

0 comments on commit f0fa814

Please sign in to comment.