Skip to content

Commit

Permalink
Merge pull request #68 from bcgov/refactor/store
Browse files Browse the repository at this point in the history
SubmissionView to store + minor general refactors
  • Loading branch information
kyle1morel authored Apr 23, 2024
2 parents 7f2d1e3 + 15465bb commit 8a1bcce
Show file tree
Hide file tree
Showing 27 changed files with 506 additions and 489 deletions.
2 changes: 1 addition & 1 deletion app/src/controllers/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const controller = {
req.body.mimeType,
req.body.length
);
res.status(200).json(response);
res.status(201).json(response);
} catch (e: unknown) {
next(e);
}
Expand Down
2 changes: 1 addition & 1 deletion app/src/controllers/note.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const controller = {
...body,
createdBy: userId
});
res.status(200).json(response);
res.status(201).json(response);
} catch (e: unknown) {
next(e);
}
Expand Down
2 changes: 1 addition & 1 deletion app/src/controllers/permit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const controller = {
try {
const userId = await userService.getCurrentUserId(getCurrentIdentity(req.currentUser, NIL), NIL);
const response = await permitService.createPermit({ ...(req.body as Permit), updatedBy: userId });
res.status(200).json(response);
res.status(201).json(response);
} catch (e: unknown) {
next(e);
}
Expand Down
44 changes: 22 additions & 22 deletions app/src/controllers/submission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,6 @@ import type { NextFunction, Request, Response } from '../interfaces/IExpress';
import type { ChefsFormConfig, ChefsFormConfigData, Submission, ChefsSubmissionExport, Permit } from '../types';

const controller = {
createEmptySubmission: async (req: Request, res: Response, next: NextFunction) => {
let testSubmissionId;
let submissionQuery;

// Testing for activityId collisions, which are truncated UUIDs
// If a collision is detected, generate new UUID and test again
do {
testSubmissionId = uuidv4();
submissionQuery = await submissionService.getSubmission(testSubmissionId.substring(0, 8).toUpperCase());
} while (submissionQuery);

try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const submitter = (req.currentUser?.tokenPayload as any)?.idir_username;
const result = await submissionService.createEmptySubmission(testSubmissionId, submitter);

res.status(201).json({ activityId: result.activity_id });
} catch (e: unknown) {
next(e);
}
},

checkAndStoreNewSubmissions: async () => {
const cfg = config.get('server.chefs.forms') as ChefsFormConfig;

Expand Down Expand Up @@ -175,6 +153,28 @@ const controller = {
notStored.map((x) => x.permits?.map(async (y) => await permitService.createPermit(y)));
},

createEmptySubmission: async (req: Request, res: Response, next: NextFunction) => {
let testSubmissionId;
let submissionQuery;

// Testing for activityId collisions, which are truncated UUIDs
// If a collision is detected, generate new UUID and test again
do {
testSubmissionId = uuidv4();
submissionQuery = await submissionService.getSubmission(testSubmissionId.substring(0, 8).toUpperCase());
} while (submissionQuery);

try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const submitter = (req.currentUser?.tokenPayload as any)?.idir_username;
const result = await submissionService.createEmptySubmission(testSubmissionId, submitter);

res.status(201).json({ activityId: result.activity_id });
} catch (e: unknown) {
next(e);
}
},

getStatistics: async (
req: Request<never, { dateFrom: string; dateTo: string; monthYear: string; userId: string }>,
res: Response,
Expand Down
28 changes: 20 additions & 8 deletions app/src/services/note.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ const service = {
return note.fromPrismaModel(response);
},

/**
* @function deleteNote
* Soft deletes a note by marking is as deleted
* @param {string} noteId ID of the note to delete
* @returns {Promise<Note>} The result of running the update operation
*/
deleteNote: async (noteId: string) => {
const result = await prisma.note.update({
where: {
Expand Down Expand Up @@ -76,8 +82,15 @@ const service = {
return response.map((x) => note.fromPrismaModel(x));
},

/**
* @function updateNote
* Updates a note by marking the old note as deleted and creating a new one
* @param {Note} data New Note object
* @returns {Promise<Note>} The result of running the transaction
*/
updateNote: async (data: Note) => {
return await prisma.$transaction(async (trx) => {
// Mark old note as deleted
await trx.note.update({
where: {
note_id: data.noteId
Expand All @@ -87,16 +100,15 @@ const service = {
}
});

const newNote = {
...data,
noteId: uuidv4()
};

const newCreatedNote = await trx.note.create({
data: note.toPrismaModel(newNote)
// Create new note
const response = await trx.note.create({
data: note.toPrismaModel({
...data,
noteId: uuidv4()
})
});

return note.fromPrismaModel(newCreatedNote);
return note.fromPrismaModel(response);
});
}
};
Expand Down
4 changes: 2 additions & 2 deletions app/tests/unit/controllers/document.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ describe('createDocument', () => {
// Mock service calls
const createSpy = jest.spyOn(documentService, 'createDocument');

it('should return 200 if all good', async () => {
it('should return 201 if all good', async () => {
const req = {
body: { documentId: 'abc123', activityId: '1', filename: 'testfile', mimeType: 'imgjpg', length: 1234567 },
currentUser: CURRENT_USER
Expand All @@ -56,7 +56,7 @@ describe('createDocument', () => {
req.body.mimeType,
req.body.length
);
expect(res.status).toHaveBeenCalledWith(200);
expect(res.status).toHaveBeenCalledWith(201);
expect(res.json).toHaveBeenCalledWith(created);
});

Expand Down
4 changes: 2 additions & 2 deletions app/tests/unit/controllers/note.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describe('createNote', () => {
const getCurrentIdentitySpy = jest.spyOn(utils, 'getCurrentIdentity');
const getCurrentUserIdSpy = jest.spyOn(userService, 'getCurrentUserId');

it('should return 200 if all good', async () => {
it('should return 201 if all good', async () => {
const req = {
body: {
noteId: '123-123',
Expand Down Expand Up @@ -75,7 +75,7 @@ describe('createNote', () => {
expect(getCurrentUserIdSpy).toHaveBeenCalledWith(USR_IDENTITY, NIL);
expect(createSpy).toHaveBeenCalledTimes(1);
expect(createSpy).toHaveBeenCalledWith({ ...req.body, createdBy: USR_ID });
expect(res.status).toHaveBeenCalledWith(200);
expect(res.status).toHaveBeenCalledWith(201);
expect(res.json).toHaveBeenCalledWith(created);
});

Expand Down
4 changes: 2 additions & 2 deletions app/tests/unit/controllers/permit.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describe('createPermit', () => {
const getCurrentIdentitySpy = jest.spyOn(utils, 'getCurrentIdentity');
const getCurrentUserIdSpy = jest.spyOn(userService, 'getCurrentUserId');

it('should return 200 if all good', async () => {
it('should return 201 if all good', async () => {
const now = new Date();
const req = {
body: {
Expand Down Expand Up @@ -81,7 +81,7 @@ describe('createPermit', () => {
expect(getCurrentUserIdSpy).toHaveBeenCalledWith(USR_IDENTITY, NIL);
expect(createSpy).toHaveBeenCalledTimes(1);
expect(createSpy).toHaveBeenCalledWith({ ...req.body, updatedBy: USR_ID });
expect(res.status).toHaveBeenCalledWith(200);
expect(res.status).toHaveBeenCalledWith(201);
expect(res.json).toHaveBeenCalledWith(created);
});

Expand Down
20 changes: 12 additions & 8 deletions frontend/src/components/file/DocumentCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ref } from 'vue';
import { Button, Card, useConfirm, useToast } from '@/lib/primevue';
import { documentService } from '@/services';
import { useSubmissionStore } from '@/store';
import { FILE_CATEGORIES } from '@/utils/constants';
import { formatDateLong } from '@/utils/formatters';
import { getFileCategory } from '@/utils/utils';
Expand All @@ -22,8 +23,8 @@ import type { Document } from '@/types';
// Props
type Props = {
document: Document;
deleteButton?: boolean;
document: Document;
selectable?: boolean;
selected?: boolean;
};
Expand All @@ -35,7 +36,10 @@ const props = withDefaults(defineProps<Props>(), {
});
// Emits
const emit = defineEmits(['document:clicked', 'document:deleted']);
const emit = defineEmits(['document:clicked']);
// Store
const submissionStore = useSubmissionStore();
// State
const isSelected: Ref<boolean> = ref(props.selected);
Expand All @@ -44,18 +48,18 @@ const isSelected: Ref<boolean> = ref(props.selected);
const confirm = useConfirm();
const toast = useToast();
const confirmDelete = (documentId: string, filename: string) => {
if (documentId) {
const confirmDelete = (document: Document) => {
if (document) {
confirm.require({
message: `Please confirm that you want to delete ${filename}.`,
message: `Please confirm that you want to delete ${document.filename}.`,
header: 'Delete document?',
acceptLabel: 'Confirm',
rejectLabel: 'Cancel',
accept: () => {
documentService
.deleteDocument(documentId)
.deleteDocument(document.documentId)
.then(() => {
emit('document:deleted', documentId);
submissionStore.removeDocument(document);
toast.success('Document deleted');
})
.catch((e: any) => toast.error('Failed to deleted document', e.message));
Expand Down Expand Up @@ -133,7 +137,7 @@ function onClick() {
aria-label="Delete object"
@click="
(e) => {
confirmDelete(props.document.documentId, props.document.filename);
confirmDelete(props.document);
e.stopPropagation();
}
"
Expand Down
19 changes: 8 additions & 11 deletions frontend/src/components/file/FileUpload.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import { ref } from 'vue';
import { FileUpload, useToast } from '@/lib/primevue';
import { documentService } from '@/services';
import { useConfigStore } from '@/store';
import { useConfigStore, useSubmissionStore } from '@/store';
import type { FileUploadUploaderEvent } from 'primevue/fileupload';
import type { Ref } from 'vue';
import type { Document } from '@/types';
// Props
type Props = {
Expand All @@ -17,35 +16,33 @@ type Props = {
const props = withDefaults(defineProps<Props>(), {});
const lastUploadedDocument = defineModel<Document>('lastUploadedDocument');
// Store
const { getConfig } = storeToRefs(useConfigStore());
const submissionStore = useSubmissionStore();
// State
const fileInput: Ref<any> = ref(null);
// Actions
const toast = useToast();
const onFileUploadDragAndDrop = (event: FileUploadUploaderEvent) => {
onUpload(Array.isArray(event.files) ? event.files[0] : event.files);
};
const onFileUploadClick = () => {
fileInput.value.click();
};
const onFileUploadDragAndDrop = (event: FileUploadUploaderEvent) => {
onUpload(Array.isArray(event.files) ? event.files[0] : event.files);
};
const onUpload = async (file: File) => {
try {
const response = (await documentService.createDocument(file, props.activityId, getConfig.value.coms.bucketId))
?.data;
if (response) {
lastUploadedDocument.value = response;
submissionStore.addDocument(response);
toast.success('Document uploaded');
}
toast.success('Document uploaded');
} catch (e: any) {
toast.error('Failed to upload document', e);
}
Expand Down
44 changes: 8 additions & 36 deletions frontend/src/components/note/NoteCard.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { Button, Card, Divider, useToast } from '@/lib/primevue';
import NoteModal from '@/components/note/NoteModal.vue';
import { noteService, userService } from '@/services';
import { Button, Card, Divider } from '@/lib/primevue';
import { userService } from '@/services';
import { formatDate, formatDateShort } from '@/utils/formatters';
import type { Ref } from 'vue';
Expand All @@ -16,40 +16,11 @@ type Props = {
const props = withDefaults(defineProps<Props>(), {});
// Emits
const emit = defineEmits(['note:delete', 'note:edit']);
// State
const userName: Ref<string> = ref('');
const noteModalVisible: Ref<boolean> = ref(false);
const editNoteData: Ref<Note | undefined> = ref(undefined);
const userName: Ref<string> = ref('');
// Actions
const toast = useToast();
const editNote = (note: Note) => {
editNoteData.value = note;
noteModalVisible.value = true;
};
const onNoteSubmit = async (data: Note) => {
editNoteData.value = undefined;
try {
const newNote = (await noteService.updateNote(data)).data;
emit('note:edit', newNote, data.noteId);
toast.success('Note saved');
} catch (e: any) {
toast.error('Failed to save note', e.message);
} finally {
noteModalVisible.value = false;
}
};
const onNoteDelete = (noteId: string) => {
emit('note:delete', noteId);
};
onMounted(() => {
if (props.note.createdBy) {
userService.searchUsers({ userId: [props.note.createdBy] }).then((res) => {
Expand Down Expand Up @@ -77,7 +48,7 @@ onMounted(() => {
<Button
class="p-button-outlined"
aria-label="Edit"
@click="editNote(props.note)"
@click="noteModalVisible = true"
>
<font-awesome-icon
class="pr-2"
Expand Down Expand Up @@ -132,11 +103,12 @@ onMounted(() => {
</div>
</template>
</Card>

<NoteModal
v-if="props.note && noteModalVisible"
v-model:visible="noteModalVisible"
:note="editNoteData"
@note:delete="onNoteDelete"
@note:submit="onNoteSubmit"
:activity-id="props.note.activityId"
:note="props.note"
/>
</template>

Expand Down
Loading

0 comments on commit 8a1bcce

Please sign in to comment.