Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

replaced Minio with Document Record Service #760

Open
wants to merge 4 commits into
base: feature-drs-integration
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions src/components/ContinuationIn/AuthorizationInformation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ import { AuthorizationProofIF, ExistingBusinessInfoIF } from '@/interfaces'
import { DateMixin, DocumentMixin } from '@/mixins'
import { CanJurisdictions, IntlJurisdictions, UsaJurisdiction } from '@bcrs-shared-components/jurisdiction/list-data'
import { JurisdictionLocation } from '@bcrs-shared-components/enums'
import { DocumentClasses } from '@/enums'

@Component({
components: {
Expand Down Expand Up @@ -285,13 +286,18 @@ export default class AuthorizationInformation extends Mixins(DateMixin, Document
if (!documentKey || !documentName) return // safety check

this.isDownloading = true
await this.downloadDocument(documentKey, documentName).catch(error => {
await this.downloadDocumentFromDRS(
documentKey,
documentName,
DocumentClasses.CORP
).catch(error => {
// eslint-disable-next-line no-console
console.log('fetchDocument() error =', error)
console.log('downloadDocument() error =', error)
this.errorDialogTitle = 'Unable to download document'
this.errorDialogText = 'We were unable to download your document. If this error persists, please contact us.'
this.errorDialogText = 'An error occurred while downloading the document. Please try again.'
this.errorDialog = true
})

this.isDownloading = false
}

Expand Down
55 changes: 33 additions & 22 deletions src/components/ContinuationIn/AuthorizationProof.vue
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ import { Action, Getter } from 'pinia-class'
import { StatusCodes } from 'http-status-codes'
import { useStore } from '@/store/store'
import { DocumentMixin } from '@/mixins'
import { AuthorizationProofIF, ExistingBusinessInfoIF, PresignedUrlIF } from '@/interfaces'
import { AuthorizationProofIF, ExistingBusinessInfoIF } from '@/interfaces'
import { FilingStatus } from '@/enums'
import FileUploadPreview from '@/components/common/FileUploadPreview.vue'
import AutoResize from 'vue-auto-resize'
Expand All @@ -183,14 +183,17 @@ export default class AuthorizationProof extends Mixins(DocumentMixin) {
$refs!: {
fileUploadPreview: FileUploadPreview
}

@Getter(useStore) getBusinessId!: string
@Getter(useStore) getTempId!: string
@Getter(useStore) getContinuationInAuthorizationProof!: AuthorizationProofIF
@Getter(useStore) getExistingBusinessInfo!: ExistingBusinessInfoIF
@Getter(useStore) getFilingStatus!: FilingStatus
@Getter(useStore) getKeycloakGuid!: string
@Getter(useStore) getShowErrors!: boolean
@Getter(useStore) getContinuationInConsumerDocumentId!: string

@Action(useStore) setContinuationAuthorization!: (x: AuthorizationProofIF) => void
@Action(useStore) setContinuationConsumerDocumentId!: (x: string) => void

// Local properties
authorization = null as AuthorizationProofIF
Expand Down Expand Up @@ -277,33 +280,40 @@ export default class AuthorizationProof extends Mixins(DocumentMixin) {
return // don't add to array
}

// try to upload to Minio
let psu: PresignedUrlIF
// try to upload to Document Record Service
try {
this.isDocumentLoading = true
psu = await this.getPresignedUrl(file.name)
const res = await this.uploadToUrl(psu.preSignedUrl, file, psu.key, this.getKeycloakGuid)
if (!res || res.status !== StatusCodes.OK) throw new Error()
const res = await this.uploadDocumentToDRS(
file,
this.documentTypes.contInAuthorization.class,
this.documentTypes.contInAuthorization.type,
this.getTempId,
this.getContinuationInConsumerDocumentId
severinbeauvais marked this conversation as resolved.
Show resolved Hide resolved
)

if (!res || ![StatusCodes.OK, StatusCodes.CREATED].includes(res.status)) throw new Error()

// add file to array
this.authorization.files.push({
file: {
name: file.name,
lastModified: file.lastModified,
size: file.size
} as File,
fileKey: res.data.documentServiceId,
fileName: file.name
})

this.setContinuationConsumerDocumentId(res.data.consumerDocumentId)

this.isFileAdded = true
} catch {
// set error message
this.customErrorMessage = this.UPLOAD_FAILED_MESSAGE
return // don't add to array
} finally {
this.isDocumentLoading = false
}

// add file to array
this.authorization.files.push({
file: {
name: file.name,
lastModified: file.lastModified,
size: file.size
} as File,
fileKey: psu.key,
fileName: file.name
})

this.isFileAdded = true
}
}

Expand All @@ -314,8 +324,9 @@ export default class AuthorizationProof extends Mixins(DocumentMixin) {
onRemoveClicked (index = NaN): void {
// safety check
if (index >= 0) {
// delete file from Minio, not waiting for response and ignoring errors
this.deleteDocument(this.authorization.files[index].fileKey).catch(() => null)
// delete file from DRS, not waiting for response and ignoring errors
this.deleteDocumentFromDRS(this.authorization.files[index].fileKey).catch((res) => console.error(res.data))

// remove file from array
this.authorization.files.splice(index, 1)
// clear any existing error message
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ import { Action, Getter } from 'pinia-class'
import { StatusCodes } from 'http-status-codes'
import { useStore } from '@/store/store'
import { DateMixin, DocumentMixin } from '@/mixins'
import { ExistingBusinessInfoIF, PresignedUrlIF } from '@/interfaces'
import { ExistingBusinessInfoIF } from '@/interfaces'
import FileUploadPreview from '../common/FileUploadPreview.vue'

@Component({
Expand All @@ -115,9 +115,12 @@ export default class UnlimitedLiabilityCorporationInformation extends Mixins(Dat
@Getter(useStore) getExistingBusinessInfo!: ExistingBusinessInfoIF
@Getter(useStore) getKeycloakGuid!: string
@Getter(useStore) getShowErrors!: boolean
@Getter(useStore) getContinuationInConsumerDocumentId!: string
@Getter(useStore) getTempId!: string

@Action(useStore) setExistingBusinessInfo!: (x: ExistingBusinessInfoIF) => void
@Action(useStore) setHaveChanges!: (x: boolean) => void
@Action(useStore) setContinuationConsumerDocumentId!: (x: string) => void

// Local properties
customErrorMessage = ''
Expand Down Expand Up @@ -164,13 +167,28 @@ export default class UnlimitedLiabilityCorporationInformation extends Mixins(Dat
return // don't add to array
}

// try to upload to Minio
let psu: PresignedUrlIF
// try to upload to Document Record Service
try {
this.isDocumentLoading = true
psu = await this.getPresignedUrl(file.name)
const res = await this.uploadToUrl(psu.preSignedUrl, file, psu.key, this.getKeycloakGuid)
if (!res || res.status !== StatusCodes.OK) throw new Error()
const res = await this.uploadDocumentToDRS(
file,
this.documentTypes.affidavitDocument.class,
this.documentTypes.affidavitDocument.type,
this.getTempId,
this.getContinuationInConsumerDocumentId
)
if (!res || ![StatusCodes.OK, StatusCodes.CREATED].includes(res.status)) throw new Error()

// add properties reactively to business object
this.$set(this.getExistingBusinessInfo, 'affidavitFile', {
name: file.name,
lastModified: file.lastModified,
size: file.size
} as File)
this.$set(this.getExistingBusinessInfo, 'affidavitFileKey', res.data.documentServiceId)
this.$set(this.getExistingBusinessInfo, 'affidavitFileName', file.name)

this.setContinuationConsumerDocumentId(res.data.consumerDocumentId)
} catch {
// set error message
this.customErrorMessage = this.UPLOAD_FAILED_MESSAGE
Expand All @@ -179,15 +197,6 @@ export default class UnlimitedLiabilityCorporationInformation extends Mixins(Dat
this.isDocumentLoading = false
}

// add properties reactively to business object
this.$set(this.getExistingBusinessInfo, 'affidavitFile', {
name: file.name,
lastModified: file.lastModified,
size: file.size
} as File)
this.$set(this.getExistingBusinessInfo, 'affidavitFileKey', psu.key)
this.$set(this.getExistingBusinessInfo, 'affidavitFileName', file.name)

// user has changed something
this.setHaveChanges(true)
}
Expand All @@ -198,8 +207,8 @@ export default class UnlimitedLiabilityCorporationInformation extends Mixins(Dat
* May also be called by parent component to remove the file info.
*/
onRemoveClicked (): void {
// delete file from Minio, not waiting for response and ignoring errors
this.deleteDocument(this.getExistingBusinessInfo.affidavitFileKey).catch(() => null)
// delete file from DRS, not waiting for response and ignoring errors
this.deleteDocumentFromDRS(this.getExistingBusinessInfo.affidavitFileKey).catch((res) => console.error(res.data))

// delete properties reactively
this.$delete(this.getExistingBusinessInfo, 'affidavitFile')
Expand Down
12 changes: 12 additions & 0 deletions src/constants/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
import { DocumentClasses } from '@/enums'

export const ADDRESSCHANGED = 'addressChanged'
export const ISFUTUREEFFECTIVE = 'isFutureEffective'
export const ISIMMEDIATE = 'isImmediate'
export const DOCUMENT_TYPES = {
contInAuthorization: {
class: DocumentClasses.CORP,
type: 'CNTO'
},
affidavitDocument: {
class: DocumentClasses.CORP,
type: 'DIRECTOR_AFFIDAVIT'
}
}
3 changes: 3 additions & 0 deletions src/enums/documentTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export enum DocumentClasses {
CORP = 'CORP'
}
1 change: 1 addition & 0 deletions src/enums/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export * from './routeNames'
export * from './ruleIds'
export * from './views'
export * from './errorTypes'
export * from './documentTypes'

// external enums
export {
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/filing-interfaces/filing-interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export interface ContinuationInFilingIF {
incorporationDate: string // YYYY-MM-DD
taxId?: string // aka Business Number
affidavitFile?: File
affidavitFileKey?: string
affidavitFileKey?: string // documentServiceId
affidavitFileName?: string
}
authorization?: AuthorizationProofIF
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export interface AuthorizationProofIF {
fileKey: string
fileName: string
}>
consumerDocumentId?: string
Copy link
Collaborator

@severinbeauvais severinbeauvais Jan 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this name consistent with documentServiceId (in filing-interfaces.ts and elsewhere)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have both documentServiceId and consumerDocumentId as well.
Thanks

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about this documentServiceId + documentConsumerId.

Is the first one "service id" and second one "document id"? If yes then what you have makes sense.

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export interface ContinuationInStateIF {
continuationAuthorizationPageValid: boolean
authorizationProof: AuthorizationProofIF
existingBusinessInfo: ExistingBusinessInfoIF
consumerDocumentId: string
severinbeauvais marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { EntityStates } from '@bcrs-shared-components/enums'

export interface ExistingBusinessInfoIF {
affidavitFile?: File // only used by UI
affidavitFileKey?: string
affidavitFileKey?: string // documentServiceId
affidavitFileName?: string
bcRegistrationDate?: string // expro only (ISO date-time: '2007-04-25T22:42:42-00:00')
bcRegistrationNumber?: string // expro only (aka Identifier)
Expand Down
91 changes: 91 additions & 0 deletions src/mixins/document-mixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { AxiosResponse } from 'axios'
import { AxiosInstance as axios } from '@/utils'
import { PresignedUrlIF, PdfInfoIF } from '@/interfaces'
import { PdfPageSize } from '@/enums'
import { DOCUMENT_TYPES } from '@/constants'
import * as pdfjs from 'pdfjs-dist/legacy/build/pdf'

@Component({})
Expand All @@ -19,13 +20,15 @@ export default class DocumentMixin extends Vue {
}

pdfjsLib: any
documentTypes: any

// use beforeCreate() instead of created() to avoid type conflict with components that use this mixin
async beforeCreate (): Promise<void> {
// NB: we load the lib and worker this way to avoid a memory leak (esp in unit tests)
// NB: must use legacy build for unit tests not running in Node 18+
this.pdfjsLib = pdfjs
this.pdfjsLib.GlobalWorkerOptions.workerSrc = await import('pdfjs-dist/legacy/build/pdf.worker.entry')
this.documentTypes = DOCUMENT_TYPES
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For cleaner code, I think you can actually do import { DOCUMENT_TYPES as documentTypes } from ....

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used to use documentTypes in the components that extend documentMixin and that's why I redefined documentTypes.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My point is, instead of using a local variable to rename DOCUMENT_TYPES, you can import it with the name you want, as per my example.

}

/**
Expand Down Expand Up @@ -192,4 +195,92 @@ export default class DocumentMixin extends Vue {
if (sizeKB > 1) return `${sizeKB.toFixed(0)} KB`
return `${size} bytes`
}

/**
* Uploads the specified file to Document Record Service.
* @param file the file to upload
* @param documentClass the document class defined for the document service. e.g. 'CORP'
* @param documentType the type of document. e.g. 'CNTA'
* @param temPid the temp business identifier
* @param consumerDocumentId the identifier of one or more documents associated with the filing.
severinbeauvais marked this conversation as resolved.
Show resolved Hide resolved
* @returns a promise to return the axios response or the error response
*/
async uploadDocumentToDRS (
document: File,
documentClass: string,
documentType: string,
tempId: string,
consumerDocumentId: string = undefined
): Promise<AxiosResponse> {
const consumerFilingDate = new Date().toISOString()

// Set request params.
let url = `${sessionStorage.getItem('DRS_API_URL')}/documents/${documentClass}/${documentType}`
url += `?consumerFilingDate=${consumerFilingDate}&consumerFilename=${document.name}`
url += `&consumerIdentifier=${tempId}`
if (consumerDocumentId) {
url += `&consumerDocumentId=${consumerDocumentId}`
}

const headers = {
'x-apikey': sessionStorage.getItem('DRS_API_KEY'),
'Account-Id': sessionStorage.getItem('DRS_ACCOUNT_ID'),
'Content-Type': 'application/pdf'
}
return axios.post(url, document, { headers: headers })
.then(response => {
return response
}).catch(error => {
return error.response
})
}

/**
* Deletes a document from Document Record Service.
* @param documentServiceId the unique identifier of document on Document Record Service
* @returns a promise to return the axios response or the error response
*/
async deleteDocumentFromDRS (documentServiceId: string): Promise<AxiosResponse> {
// safety checks
if (!documentServiceId) {
throw new Error('Invalid parameters')
}

const url = `documents/drs/${documentServiceId}`

return axios.delete(url)
}

/**
* Download the specified file from Document Record Service.
* @param documentKey the unique id on Document Record Service
* @param documentClass the document class defined for the document service. e.g. 'CORP'
* @param documentName the document name to download
* @returns void
*/
async downloadDocumentFromDRS (documentKey: string,
documentName: string,
documentClass: string
): Promise<void> {
// safety checks
if (!documentKey || !documentName) {
throw new Error('Invalid parameters')
}
const url = `documents/drs/${documentClass}/${documentKey}`

axios.get(url).then(response => {
if (!response) throw new Error('Null response')
const link = document.createElement('a')
link.href = response.data.documentURL
link.download = documentName
link.target = '_blank' // This opens the link in a new browser tab

// Append to the document and trigger the download
document.body.appendChild(link)
link.click()

// Remove the link after the download is triggered
document.body.removeChild(link)
})
}
}
Loading
Loading