File refs in one table
Added a file service Refactored evidence and vendor compliance associated services and resolvers Created SQL migration scripts Updated GraphQL schema accordingly and the UI components Removed unused methods and resolvers for evidence service Signed-off-by: Yannis Varni <yannis@edinomis.fr>
This commit is contained in:
committed by
Sacha Al Himdani
parent
3822b1916c
commit
ff7ba08fc1
@@ -5,9 +5,12 @@ export const evidenceFileQuery = graphql`
|
||||
node(id: $evidenceId) {
|
||||
... on Evidence {
|
||||
id
|
||||
mimeType
|
||||
filename
|
||||
fileUrl
|
||||
file {
|
||||
mimeType
|
||||
fileName
|
||||
size
|
||||
downloadUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<0c4d6a4bd477e15ae3c627cf338a99dd>>
|
||||
* @generated SignedSource<<843df409ed1ee9ba19760921dbd6e8df>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -14,10 +14,13 @@ export type EvidenceGraphFileQuery$variables = {
|
||||
};
|
||||
export type EvidenceGraphFileQuery$data = {
|
||||
readonly node: {
|
||||
readonly fileUrl?: string | null | undefined;
|
||||
readonly filename?: string;
|
||||
readonly file?: {
|
||||
readonly downloadUrl: string;
|
||||
readonly fileName: string;
|
||||
readonly mimeType: string;
|
||||
readonly size: any;
|
||||
} | null | undefined;
|
||||
readonly id?: string;
|
||||
readonly mimeType?: string;
|
||||
};
|
||||
};
|
||||
export type EvidenceGraphFileQuery = {
|
||||
@@ -58,14 +61,21 @@ v4 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "filename",
|
||||
"name": "fileName",
|
||||
"storageKey": null
|
||||
},
|
||||
v5 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fileUrl",
|
||||
"name": "size",
|
||||
"storageKey": null
|
||||
},
|
||||
v6 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "downloadUrl",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
@@ -87,9 +97,21 @@ return {
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/)
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "File",
|
||||
"kind": "LinkedField",
|
||||
"name": "file",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/),
|
||||
(v6/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Evidence",
|
||||
"abstractKey": null
|
||||
@@ -126,9 +148,22 @@ return {
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/)
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "File",
|
||||
"kind": "LinkedField",
|
||||
"name": "file",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/),
|
||||
(v6/*: any*/),
|
||||
(v2/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Evidence",
|
||||
"abstractKey": null
|
||||
@@ -139,16 +174,16 @@ return {
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "db46f9bf993514c7bc89845e6d6459f8",
|
||||
"cacheID": "780e108a52d2fcbde6653de5d2ee058b",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "EvidenceGraphFileQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query EvidenceGraphFileQuery(\n $evidenceId: ID!\n) {\n node(id: $evidenceId) {\n __typename\n ... on Evidence {\n id\n mimeType\n filename\n fileUrl\n }\n id\n }\n}\n"
|
||||
"text": "query EvidenceGraphFileQuery(\n $evidenceId: ID!\n) {\n node(id: $evidenceId) {\n __typename\n ... on Evidence {\n id\n file {\n mimeType\n fileName\n size\n downloadUrl\n id\n }\n }\n id\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "3c9aa6ff338d2380a7a03b8ea61e83e0";
|
||||
(node as any).hash = "186386e82f82feb9c5b5a54f9d937903";
|
||||
|
||||
export default node;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<074d65be2a59b6b8284ff6be0f75707d>>
|
||||
* @generated SignedSource<<1f413a42110dae78274cf3250fa4db4e>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -594,22 +594,34 @@ return {
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "filename",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "mimeType",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "size",
|
||||
"concreteType": "File",
|
||||
"kind": "LinkedField",
|
||||
"name": "file",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fileName",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "mimeType",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "size",
|
||||
"storageKey": null
|
||||
},
|
||||
(v2/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
@@ -660,12 +672,12 @@ return {
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "6146a6fada0328c7b69652646212be05",
|
||||
"cacheID": "d20815cee9ca8dec2e09fcefdd54a68a",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "MeasureGraphNodeQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query MeasureGraphNodeQuery(\n $measureId: ID!\n) {\n node(id: $measureId) {\n __typename\n ... on Measure {\n id\n name\n description\n state\n category\n evidencesInfos: evidences(first: 0) {\n totalCount\n }\n risksInfos: risks(first: 0) {\n totalCount\n }\n tasksInfos: tasks(first: 0) {\n totalCount\n }\n controlsInfos: controls(first: 0) {\n totalCount\n }\n ...MeasureRisksTabFragment\n ...MeasureTasksTabFragment\n ...MeasureControlsTabFragment\n ...MeasureFormDialogMeasureFragment\n ...MeasureEvidencesTabFragment\n }\n id\n }\n}\n\nfragment LinkedControlsCardFragment on Control {\n id\n name\n sectionTitle\n framework {\n id\n name\n }\n}\n\nfragment LinkedRisksCardFragment on Risk {\n id\n name\n inherentRiskScore\n residualRiskScore\n}\n\nfragment MeasureControlsTabFragment on Measure {\n id\n controls(first: 20) {\n edges {\n node {\n id\n ...LinkedControlsCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment MeasureEvidencesTabFragment on Measure {\n id\n evidences(first: 50) {\n edges {\n node {\n id\n filename\n mimeType\n ...MeasureEvidencesTabFragment_evidence\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment MeasureEvidencesTabFragment_evidence on Evidence {\n id\n filename\n size\n type\n createdAt\n mimeType\n}\n\nfragment MeasureFormDialogMeasureFragment on Measure {\n id\n description\n name\n category\n state\n}\n\nfragment MeasureRisksTabFragment on Measure {\n id\n risks(first: 100) {\n edges {\n node {\n id\n ...LinkedRisksCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment MeasureTasksTabFragment on Measure {\n tasks(first: 100) {\n edges {\n node {\n id\n name\n state\n description\n ...TaskFormDialogFragment\n assignedTo {\n id\n fullName\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment TaskFormDialogFragment on Task {\n id\n description\n name\n state\n timeEstimate\n deadline\n assignedTo {\n id\n }\n measure {\n id\n }\n}\n"
|
||||
"text": "query MeasureGraphNodeQuery(\n $measureId: ID!\n) {\n node(id: $measureId) {\n __typename\n ... on Measure {\n id\n name\n description\n state\n category\n evidencesInfos: evidences(first: 0) {\n totalCount\n }\n risksInfos: risks(first: 0) {\n totalCount\n }\n tasksInfos: tasks(first: 0) {\n totalCount\n }\n controlsInfos: controls(first: 0) {\n totalCount\n }\n ...MeasureRisksTabFragment\n ...MeasureTasksTabFragment\n ...MeasureControlsTabFragment\n ...MeasureFormDialogMeasureFragment\n ...MeasureEvidencesTabFragment\n }\n id\n }\n}\n\nfragment LinkedControlsCardFragment on Control {\n id\n name\n sectionTitle\n framework {\n id\n name\n }\n}\n\nfragment LinkedRisksCardFragment on Risk {\n id\n name\n inherentRiskScore\n residualRiskScore\n}\n\nfragment MeasureControlsTabFragment on Measure {\n id\n controls(first: 20) {\n edges {\n node {\n id\n ...LinkedControlsCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment MeasureEvidencesTabFragment on Measure {\n id\n evidences(first: 50) {\n edges {\n node {\n id\n file {\n fileName\n mimeType\n size\n id\n }\n ...MeasureEvidencesTabFragment_evidence\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment MeasureEvidencesTabFragment_evidence on Evidence {\n id\n file {\n fileName\n mimeType\n size\n id\n }\n type\n createdAt\n}\n\nfragment MeasureFormDialogMeasureFragment on Measure {\n id\n description\n name\n category\n state\n}\n\nfragment MeasureRisksTabFragment on Measure {\n id\n risks(first: 100) {\n edges {\n node {\n id\n ...LinkedRisksCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment MeasureTasksTabFragment on Measure {\n tasks(first: 100) {\n edges {\n node {\n id\n name\n state\n description\n ...TaskFormDialogFragment\n assignedTo {\n id\n fullName\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment TaskFormDialogFragment on Task {\n id\n description\n name\n state\n timeEstimate\n deadline\n assignedTo {\n id\n }\n measure {\n id\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<d72032da6df08b84ce9c7ec87b950d08>>
|
||||
* @generated SignedSource<<fdfadfbc4f5fdecf5f70a8c92d3ded88>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -145,7 +145,7 @@ v13 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fileUrl",
|
||||
"name": "fileName",
|
||||
"storageKey": null
|
||||
},
|
||||
v14 = {
|
||||
@@ -229,14 +229,14 @@ v23 = {
|
||||
},
|
||||
v24 = [
|
||||
(v3/*: any*/),
|
||||
(v13/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fileName",
|
||||
"name": "fileUrl",
|
||||
"storageKey": null
|
||||
},
|
||||
(v13/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
@@ -500,12 +500,31 @@ return {
|
||||
"name": "reportName",
|
||||
"storageKey": null
|
||||
},
|
||||
(v13/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fileSize",
|
||||
"concreteType": "File",
|
||||
"kind": "LinkedField",
|
||||
"name": "file",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v13/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "mimeType",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "size",
|
||||
"storageKey": null
|
||||
},
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
(v9/*: any*/)
|
||||
@@ -791,12 +810,12 @@ return {
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "4a112f861864990f5022d279ba8d43d4",
|
||||
"cacheID": "faee4f059ef9baab46d5f2420e36bb60",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "VendorGraphNodeQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query VendorGraphNodeQuery(\n $vendorId: ID!\n $organizationId: ID!\n) {\n node(id: $vendorId) {\n __typename\n ... on Vendor {\n id\n snapshotId\n name\n websiteUrl\n ...useVendorFormFragment\n ...VendorComplianceTabFragment\n ...VendorContactsTabFragment\n ...VendorServicesTabFragment\n ...VendorRiskAssessmentTabFragment\n ...VendorOverviewTabBusinessAssociateAgreementFragment\n ...VendorOverviewTabDataPrivacyAgreementFragment\n }\n id\n }\n viewer {\n user {\n id\n people(organizationId: $organizationId) {\n id\n }\n }\n id\n }\n}\n\nfragment VendorComplianceTabFragment on Vendor {\n complianceReports(first: 50) {\n edges {\n node {\n id\n ...VendorComplianceTabFragment_report\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment VendorComplianceTabFragment_report on VendorComplianceReport {\n id\n reportDate\n validUntil\n reportName\n fileUrl\n fileSize\n}\n\nfragment VendorContactsTabFragment on Vendor {\n contacts(first: 50) {\n edges {\n node {\n id\n ...VendorContactsTabFragment_contact\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment VendorContactsTabFragment_contact on VendorContact {\n id\n fullName\n email\n phone\n role\n createdAt\n updatedAt\n}\n\nfragment VendorOverviewTabBusinessAssociateAgreementFragment on Vendor {\n businessAssociateAgreement {\n id\n fileName\n fileUrl\n validFrom\n validUntil\n createdAt\n }\n}\n\nfragment VendorOverviewTabDataPrivacyAgreementFragment on Vendor {\n dataPrivacyAgreement {\n id\n fileName\n fileUrl\n validFrom\n validUntil\n createdAt\n }\n}\n\nfragment VendorRiskAssessmentTabFragment on Vendor {\n id\n riskAssessments(first: 50) {\n edges {\n node {\n id\n ...VendorRiskAssessmentTabFragment_assessment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment VendorRiskAssessmentTabFragment_assessment on VendorRiskAssessment {\n id\n createdAt\n expiresAt\n dataSensitivity\n businessImpact\n notes\n}\n\nfragment VendorServicesTabFragment on Vendor {\n services(first: 50) {\n edges {\n node {\n id\n ...VendorServicesTabFragment_service\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment VendorServicesTabFragment_service on VendorService {\n id\n name\n description\n createdAt\n updatedAt\n}\n\nfragment useVendorFormFragment on Vendor {\n id\n name\n description\n category\n statusPageUrl\n termsOfServiceUrl\n privacyPolicyUrl\n serviceLevelAgreementUrl\n dataProcessingAgreementUrl\n websiteUrl\n legalName\n headquarterAddress\n certifications\n countries\n securityPageUrl\n trustPageUrl\n businessOwner {\n id\n }\n securityOwner {\n id\n }\n}\n"
|
||||
"text": "query VendorGraphNodeQuery(\n $vendorId: ID!\n $organizationId: ID!\n) {\n node(id: $vendorId) {\n __typename\n ... on Vendor {\n id\n snapshotId\n name\n websiteUrl\n ...useVendorFormFragment\n ...VendorComplianceTabFragment\n ...VendorContactsTabFragment\n ...VendorServicesTabFragment\n ...VendorRiskAssessmentTabFragment\n ...VendorOverviewTabBusinessAssociateAgreementFragment\n ...VendorOverviewTabDataPrivacyAgreementFragment\n }\n id\n }\n viewer {\n user {\n id\n people(organizationId: $organizationId) {\n id\n }\n }\n id\n }\n}\n\nfragment VendorComplianceTabFragment on Vendor {\n complianceReports(first: 50) {\n edges {\n node {\n id\n ...VendorComplianceTabFragment_report\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment VendorComplianceTabFragment_report on VendorComplianceReport {\n id\n reportDate\n validUntil\n reportName\n file {\n fileName\n mimeType\n size\n id\n }\n}\n\nfragment VendorContactsTabFragment on Vendor {\n contacts(first: 50) {\n edges {\n node {\n id\n ...VendorContactsTabFragment_contact\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment VendorContactsTabFragment_contact on VendorContact {\n id\n fullName\n email\n phone\n role\n createdAt\n updatedAt\n}\n\nfragment VendorOverviewTabBusinessAssociateAgreementFragment on Vendor {\n businessAssociateAgreement {\n id\n fileName\n fileUrl\n validFrom\n validUntil\n createdAt\n }\n}\n\nfragment VendorOverviewTabDataPrivacyAgreementFragment on Vendor {\n dataPrivacyAgreement {\n id\n fileName\n fileUrl\n validFrom\n validUntil\n createdAt\n }\n}\n\nfragment VendorRiskAssessmentTabFragment on Vendor {\n id\n riskAssessments(first: 50) {\n edges {\n node {\n id\n ...VendorRiskAssessmentTabFragment_assessment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment VendorRiskAssessmentTabFragment_assessment on VendorRiskAssessment {\n id\n createdAt\n expiresAt\n dataSensitivity\n businessImpact\n notes\n}\n\nfragment VendorServicesTabFragment on Vendor {\n services(first: 50) {\n edges {\n node {\n id\n ...VendorServicesTabFragment_service\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment VendorServicesTabFragment_service on VendorService {\n id\n name\n description\n createdAt\n updatedAt\n}\n\nfragment useVendorFormFragment on Vendor {\n id\n name\n description\n category\n statusPageUrl\n termsOfServiceUrl\n privacyPolicyUrl\n serviceLevelAgreementUrl\n dataProcessingAgreementUrl\n websiteUrl\n legalName\n headquarterAddress\n certifications\n countries\n securityPageUrl\n trustPageUrl\n businessOwner {\n id\n }\n securityOwner {\n id\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -50,7 +50,7 @@ function DownloadLink({ evidenceId, onClose }: Props) {
|
||||
const evidence = data.node;
|
||||
|
||||
useEffect(() => {
|
||||
downloadFile(evidence.fileUrl, evidence.filename ?? "evidence");
|
||||
downloadFile(evidence.file?.downloadUrl, evidence.file?.fileName ?? "evidence");
|
||||
onClose();
|
||||
}, [evidence]);
|
||||
|
||||
|
||||
@@ -75,13 +75,13 @@ function EvidencePreviewContent({
|
||||
const { __ } = useTranslate();
|
||||
const { toast } = useToast();
|
||||
const isUriFile =
|
||||
evidence.mimeType === "text/uri-list" || evidence.mimeType === "text/uri";
|
||||
evidence.file?.mimeType === "text/uri-list" || evidence.file?.mimeType === "text/uri";
|
||||
useEffect(() => {
|
||||
if (!isUriFile) {
|
||||
return;
|
||||
}
|
||||
const abortController = new AbortController();
|
||||
fetchUrlFromUriFile(evidence.fileUrl ?? "", {
|
||||
fetchUrlFromUriFile(evidence.file?.downloadUrl ?? "", {
|
||||
signal: abortController.signal,
|
||||
})
|
||||
.then((url) => {
|
||||
@@ -101,9 +101,9 @@ function EvidencePreviewContent({
|
||||
return () => {
|
||||
abortController.abort();
|
||||
};
|
||||
}, [evidence.fileUrl, isUriFile]);
|
||||
}, [evidence.file?.downloadUrl, isUriFile]);
|
||||
|
||||
if (!evidence.fileUrl) {
|
||||
if (!evidence.file?.downloadUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -115,22 +115,22 @@ function EvidencePreviewContent({
|
||||
);
|
||||
}
|
||||
|
||||
if (evidence.mimeType?.startsWith("image/")) {
|
||||
if (evidence.file.mimeType?.startsWith("image/")) {
|
||||
return (
|
||||
<img
|
||||
src={evidence.fileUrl}
|
||||
alt={evidence.filename}
|
||||
src={evidence.file.downloadUrl}
|
||||
alt={evidence.file.fileName}
|
||||
className="max-h-[70vh] object-contain"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (evidence.mimeType?.includes("pdf")) {
|
||||
if (evidence.file.mimeType?.includes("pdf")) {
|
||||
return (
|
||||
<iframe
|
||||
src={evidence.fileUrl}
|
||||
src={evidence.file.downloadUrl}
|
||||
className="w-full h-[70vh]"
|
||||
title={evidence.filename}
|
||||
title={evidence.file.fileName}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -141,10 +141,10 @@ function EvidencePreviewContent({
|
||||
<p className="text-txt-secondary text-center">
|
||||
{__("Preview not available for this file type") +
|
||||
" " +
|
||||
evidence.mimeType}
|
||||
evidence.file.mimeType}
|
||||
</p>
|
||||
<Button asChild variant="secondary" icon={IconArrowInbox}>
|
||||
<a href={evidence.fileUrl} target="_blank">
|
||||
<a href={evidence.file.downloadUrl} target="_blank">
|
||||
{__("Download File")}
|
||||
</a>
|
||||
</Button>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<9297f85591b8ab4723a207f6db6a27f6>>
|
||||
* @generated SignedSource<<3ed9b93b29f7a5117adbb33ad359f34e>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -148,15 +148,34 @@ return {
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "filename",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "size",
|
||||
"concreteType": "File",
|
||||
"kind": "LinkedField",
|
||||
"name": "file",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fileName",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "mimeType",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "size",
|
||||
"storageKey": null
|
||||
},
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
@@ -172,13 +191,6 @@ return {
|
||||
"kind": "ScalarField",
|
||||
"name": "createdAt",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "mimeType",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
@@ -208,12 +220,12 @@ return {
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "5206b6576cacb45f5b42305bb1bb7df1",
|
||||
"cacheID": "4224b79c60bb9be5f4768da436659656",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "CreateEvidenceDialogUploadMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation CreateEvidenceDialogUploadMutation(\n $input: UploadMeasureEvidenceInput!\n) {\n uploadMeasureEvidence(input: $input) {\n evidenceEdge {\n node {\n id\n ...MeasureEvidencesTabFragment_evidence\n }\n }\n }\n}\n\nfragment MeasureEvidencesTabFragment_evidence on Evidence {\n id\n filename\n size\n type\n createdAt\n mimeType\n}\n"
|
||||
"text": "mutation CreateEvidenceDialogUploadMutation(\n $input: UploadMeasureEvidenceInput!\n) {\n uploadMeasureEvidence(input: $input) {\n evidenceEdge {\n node {\n id\n ...MeasureEvidencesTabFragment_evidence\n }\n }\n }\n}\n\nfragment MeasureEvidencesTabFragment_evidence on Evidence {\n id\n file {\n fileName\n mimeType\n size\n id\n }\n type\n createdAt\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -56,8 +56,11 @@ export const evidencesFragment = graphql`
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
filename
|
||||
mimeType
|
||||
file {
|
||||
fileName
|
||||
mimeType
|
||||
size
|
||||
}
|
||||
...MeasureEvidencesTabFragment_evidence
|
||||
}
|
||||
}
|
||||
@@ -68,11 +71,13 @@ export const evidencesFragment = graphql`
|
||||
export const evidenceFragment = graphql`
|
||||
fragment MeasureEvidencesTabFragment_evidence on Evidence {
|
||||
id
|
||||
filename
|
||||
size
|
||||
file {
|
||||
fileName
|
||||
mimeType
|
||||
size
|
||||
}
|
||||
type
|
||||
createdAt
|
||||
mimeType
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -150,7 +155,7 @@ export default function MeasureEvidencesTab() {
|
||||
navigate(baseUrl);
|
||||
}}
|
||||
evidenceId={evidence.id}
|
||||
filename={evidence.filename}
|
||||
filename={evidence.file?.fileName || ""}
|
||||
/>
|
||||
)}
|
||||
{!isSnapshotMode && (
|
||||
@@ -207,7 +212,7 @@ function EvidenceRow(props: {
|
||||
__(
|
||||
'This will permanently delete the evidence "%s". This action cannot be undone.'
|
||||
),
|
||||
evidence.filename
|
||||
evidence.file?.fileName
|
||||
),
|
||||
}
|
||||
);
|
||||
@@ -226,9 +231,9 @@ function EvidenceRow(props: {
|
||||
/>
|
||||
)}
|
||||
<Tr to={evidenceUrl}>
|
||||
<Td>{evidence.filename}</Td>
|
||||
<Td>{fileType(__, evidence)}</Td>
|
||||
<Td>{fileSize(__, evidence.size)}</Td>
|
||||
<Td>{evidence.file?.fileName}</Td>
|
||||
<Td>{fileType(__, {type: evidence.type, mimeType: evidence.file?.mimeType || ''})}</Td>
|
||||
<Td>{fileSize(__, evidence.file?.size || 0)}</Td>
|
||||
<Td>{formatDate(evidence.createdAt)}</Td>
|
||||
<Td noLink>
|
||||
{!props.hideActions && (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<6c96b0b565302f1ec1601561087b30bd>>
|
||||
* @generated SignedSource<<95a549108a1a26ccfaf2d8aae42cea73>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -15,9 +15,12 @@ export type MeasureEvidencesTabFragment$data = {
|
||||
readonly __id: string;
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly filename: string;
|
||||
readonly file: {
|
||||
readonly fileName: string;
|
||||
readonly mimeType: string;
|
||||
readonly size: any;
|
||||
} | null | undefined;
|
||||
readonly id: string;
|
||||
readonly mimeType: string;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"MeasureEvidencesTabFragment_evidence">;
|
||||
};
|
||||
}>;
|
||||
@@ -140,15 +143,33 @@ return {
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "filename",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "mimeType",
|
||||
"concreteType": "File",
|
||||
"kind": "LinkedField",
|
||||
"name": "file",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fileName",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "mimeType",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "size",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
@@ -236,6 +257,6 @@ return {
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "8e97649ca88af04aa1184e68a126cc36";
|
||||
(node as any).hash = "c80a159cbb4ac8b8855875eff00d1255";
|
||||
|
||||
export default node;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<60897aba5bbafb70288358e56b0a442a>>
|
||||
* @generated SignedSource<<0fb98c755d9aacac0ef1d508af9e7edc>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -13,10 +13,12 @@ export type EvidenceType = "FILE" | "LINK";
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type MeasureEvidencesTabFragment_evidence$data = {
|
||||
readonly createdAt: any;
|
||||
readonly filename: string;
|
||||
readonly file: {
|
||||
readonly fileName: string;
|
||||
readonly mimeType: string;
|
||||
readonly size: any;
|
||||
} | null | undefined;
|
||||
readonly id: string;
|
||||
readonly mimeType: string;
|
||||
readonly size: number;
|
||||
readonly type: EvidenceType;
|
||||
readonly " $fragmentType": "MeasureEvidencesTabFragment_evidence";
|
||||
};
|
||||
@@ -41,15 +43,33 @@ const node: ReaderFragment = {
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "filename",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "size",
|
||||
"concreteType": "File",
|
||||
"kind": "LinkedField",
|
||||
"name": "file",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fileName",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "mimeType",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "size",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
@@ -65,19 +85,12 @@ const node: ReaderFragment = {
|
||||
"kind": "ScalarField",
|
||||
"name": "createdAt",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "mimeType",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Evidence",
|
||||
"abstractKey": null
|
||||
};
|
||||
|
||||
(node as any).hash = "4b3289f3cb9d29cb48f1a370d943cd0e";
|
||||
(node as any).hash = "441ff1dd2dd62fea27050c5f550b27ea";
|
||||
|
||||
export default node;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<b2d32283d91e7ef9da2eeda3c789867f>>
|
||||
* @generated SignedSource<<b182b11e656579608aba8fb03faa0ef6>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -215,22 +215,34 @@ return {
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "filename",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "mimeType",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "size",
|
||||
"concreteType": "File",
|
||||
"kind": "LinkedField",
|
||||
"name": "file",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fileName",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "mimeType",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "size",
|
||||
"storageKey": null
|
||||
},
|
||||
(v12/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
@@ -336,16 +348,16 @@ return {
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "9899e64b590ff4c76ca994845611d77a",
|
||||
"cacheID": "45ba257dabcf1cc7dd4aa05b699c280e",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "MeasureEvidencesTabQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query MeasureEvidencesTabQuery(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 50\n $last: Int = null\n $order: EvidenceOrder = null\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...MeasureEvidencesTabFragment_16fISc\n id\n }\n}\n\nfragment MeasureEvidencesTabFragment_16fISc on Measure {\n id\n evidences(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n edges {\n node {\n id\n filename\n mimeType\n ...MeasureEvidencesTabFragment_evidence\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment MeasureEvidencesTabFragment_evidence on Evidence {\n id\n filename\n size\n type\n createdAt\n mimeType\n}\n"
|
||||
"text": "query MeasureEvidencesTabQuery(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 50\n $last: Int = null\n $order: EvidenceOrder = null\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...MeasureEvidencesTabFragment_16fISc\n id\n }\n}\n\nfragment MeasureEvidencesTabFragment_16fISc on Measure {\n id\n evidences(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n edges {\n node {\n id\n file {\n fileName\n mimeType\n size\n id\n }\n ...MeasureEvidencesTabFragment_evidence\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment MeasureEvidencesTabFragment_evidence on Evidence {\n id\n file {\n fileName\n mimeType\n size\n id\n }\n type\n createdAt\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "8e97649ca88af04aa1184e68a126cc36";
|
||||
(node as any).hash = "c80a159cbb4ac8b8855875eff00d1255";
|
||||
|
||||
export default node;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<22e7411298e2df9b5f5eae17bb0a510f>>
|
||||
* @generated SignedSource<<f851ea2f7b0fa09f081a26967bb7766e>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -371,15 +371,34 @@ return {
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fileUrl",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fileSize",
|
||||
"concreteType": "File",
|
||||
"kind": "LinkedField",
|
||||
"name": "file",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fileName",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "mimeType",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "size",
|
||||
"storageKey": null
|
||||
},
|
||||
(v2/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
(v7/*: any*/)
|
||||
@@ -523,12 +542,12 @@ return {
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "0cb0f301c3022d933fd9ee0d6319d3e4",
|
||||
"cacheID": "d0d8cc2e128b7a7b31f5e9c86e427a7f",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "ImportAssessmentDialogMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation ImportAssessmentDialogMutation(\n $input: AssessVendorInput!\n) {\n assessVendor(input: $input) {\n vendor {\n id\n name\n websiteUrl\n ...useVendorFormFragment\n ...VendorComplianceTabFragment\n ...VendorRiskAssessmentTabFragment\n }\n }\n}\n\nfragment VendorComplianceTabFragment on Vendor {\n complianceReports(first: 50) {\n edges {\n node {\n id\n ...VendorComplianceTabFragment_report\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment VendorComplianceTabFragment_report on VendorComplianceReport {\n id\n reportDate\n validUntil\n reportName\n fileUrl\n fileSize\n}\n\nfragment VendorRiskAssessmentTabFragment on Vendor {\n id\n riskAssessments(first: 50) {\n edges {\n node {\n id\n ...VendorRiskAssessmentTabFragment_assessment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment VendorRiskAssessmentTabFragment_assessment on VendorRiskAssessment {\n id\n createdAt\n expiresAt\n dataSensitivity\n businessImpact\n notes\n}\n\nfragment useVendorFormFragment on Vendor {\n id\n name\n description\n category\n statusPageUrl\n termsOfServiceUrl\n privacyPolicyUrl\n serviceLevelAgreementUrl\n dataProcessingAgreementUrl\n websiteUrl\n legalName\n headquarterAddress\n certifications\n countries\n securityPageUrl\n trustPageUrl\n businessOwner {\n id\n }\n securityOwner {\n id\n }\n}\n"
|
||||
"text": "mutation ImportAssessmentDialogMutation(\n $input: AssessVendorInput!\n) {\n assessVendor(input: $input) {\n vendor {\n id\n name\n websiteUrl\n ...useVendorFormFragment\n ...VendorComplianceTabFragment\n ...VendorRiskAssessmentTabFragment\n }\n }\n}\n\nfragment VendorComplianceTabFragment on Vendor {\n complianceReports(first: 50) {\n edges {\n node {\n id\n ...VendorComplianceTabFragment_report\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment VendorComplianceTabFragment_report on VendorComplianceReport {\n id\n reportDate\n validUntil\n reportName\n file {\n fileName\n mimeType\n size\n id\n }\n}\n\nfragment VendorRiskAssessmentTabFragment on Vendor {\n id\n riskAssessments(first: 50) {\n edges {\n node {\n id\n ...VendorRiskAssessmentTabFragment_assessment\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment VendorRiskAssessmentTabFragment_assessment on VendorRiskAssessment {\n id\n createdAt\n expiresAt\n dataSensitivity\n businessImpact\n notes\n}\n\nfragment useVendorFormFragment on Vendor {\n id\n name\n description\n category\n statusPageUrl\n termsOfServiceUrl\n privacyPolicyUrl\n serviceLevelAgreementUrl\n dataProcessingAgreementUrl\n websiteUrl\n legalName\n headquarterAddress\n certifications\n countries\n securityPageUrl\n trustPageUrl\n businessOwner {\n id\n }\n securityOwner {\n id\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -55,8 +55,11 @@ const complianceReportFragment = graphql`
|
||||
reportDate
|
||||
validUntil
|
||||
reportName
|
||||
fileUrl
|
||||
fileSize
|
||||
file {
|
||||
fileName
|
||||
mimeType
|
||||
size
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -208,7 +211,7 @@ function ReportRow(props: ReportRowProps) {
|
||||
<Td>{report.reportName}</Td>
|
||||
<Td>{formatDate(report.reportDate)}</Td>
|
||||
<Td>{formatDate(report.validUntil)}</Td>
|
||||
<Td>{fileSize(__, report.fileSize)}</Td>
|
||||
<Td>{fileSize(__, report.file?.size)}</Td>
|
||||
{!props.isSnapshotMode && (
|
||||
<Td width={50} className="text-end">
|
||||
<ActionDropdown>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<10fdd09e95fc5f2995b91c73cf9fb945>>
|
||||
* @generated SignedSource<<5fd3a3293bd92112440eef3fd292fdc1>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -236,15 +236,34 @@ return {
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fileUrl",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fileSize",
|
||||
"concreteType": "File",
|
||||
"kind": "LinkedField",
|
||||
"name": "file",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fileName",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "mimeType",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "size",
|
||||
"storageKey": null
|
||||
},
|
||||
(v12/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
(v11/*: any*/)
|
||||
@@ -336,12 +355,12 @@ return {
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "c819e002dfe9f20004fb5bfb4f95b143",
|
||||
"cacheID": "594e7edf24b110430566c9a21d1b5d29",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "ComplianceReportListQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query ComplianceReportListQuery(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 50\n $last: Int = null\n $order: VendorComplianceReportOrder = null\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...VendorComplianceTabFragment_16fISc\n id\n }\n}\n\nfragment VendorComplianceTabFragment_16fISc on Vendor {\n complianceReports(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n edges {\n node {\n id\n ...VendorComplianceTabFragment_report\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment VendorComplianceTabFragment_report on VendorComplianceReport {\n id\n reportDate\n validUntil\n reportName\n fileUrl\n fileSize\n}\n"
|
||||
"text": "query ComplianceReportListQuery(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 50\n $last: Int = null\n $order: VendorComplianceReportOrder = null\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...VendorComplianceTabFragment_16fISc\n id\n }\n}\n\nfragment VendorComplianceTabFragment_16fISc on Vendor {\n complianceReports(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n edges {\n node {\n id\n ...VendorComplianceTabFragment_report\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment VendorComplianceTabFragment_report on VendorComplianceReport {\n id\n reportDate\n validUntil\n reportName\n file {\n fileName\n mimeType\n size\n id\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<4c861a832ad2f79bd9d6ddcc364f6898>>
|
||||
* @generated SignedSource<<8cf7478142bb658e6c8d189652bbdfea>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -11,8 +11,11 @@
|
||||
import { ReaderFragment } from 'relay-runtime';
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type VendorComplianceTabFragment_report$data = {
|
||||
readonly fileSize: any;
|
||||
readonly fileUrl: string;
|
||||
readonly file: {
|
||||
readonly fileName: string;
|
||||
readonly mimeType: string;
|
||||
readonly size: any;
|
||||
} | null | undefined;
|
||||
readonly id: string;
|
||||
readonly reportDate: any;
|
||||
readonly reportName: string;
|
||||
@@ -61,15 +64,33 @@ const node: ReaderFragment = {
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fileUrl",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fileSize",
|
||||
"concreteType": "File",
|
||||
"kind": "LinkedField",
|
||||
"name": "file",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fileName",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "mimeType",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "size",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
@@ -77,6 +98,6 @@ const node: ReaderFragment = {
|
||||
"abstractKey": null
|
||||
};
|
||||
|
||||
(node as any).hash = "92ad78c9e4b4add9b1f622485d81a6ff";
|
||||
(node as any).hash = "18f52de61577c8b1c61784d2404c24ae";
|
||||
|
||||
export default node;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<6093b0bc54fa90962376639ec87fa149>>
|
||||
* @generated SignedSource<<68f2a5a67287a554c3f45ddfc856b6ea>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -172,15 +172,34 @@ return {
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fileUrl",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fileSize",
|
||||
"concreteType": "File",
|
||||
"kind": "LinkedField",
|
||||
"name": "file",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "fileName",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "mimeType",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "size",
|
||||
"storageKey": null
|
||||
},
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
@@ -211,12 +230,12 @@ return {
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "fb9be687deba8567502e207b0969b2b5",
|
||||
"cacheID": "193fdb014a3493e0b0d56a96309afae3",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "VendorComplianceTabUploadReportMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation VendorComplianceTabUploadReportMutation(\n $input: UploadVendorComplianceReportInput!\n) {\n uploadVendorComplianceReport(input: $input) {\n vendorComplianceReportEdge {\n node {\n id\n ...VendorComplianceTabFragment_report\n }\n }\n }\n}\n\nfragment VendorComplianceTabFragment_report on VendorComplianceReport {\n id\n reportDate\n validUntil\n reportName\n fileUrl\n fileSize\n}\n"
|
||||
"text": "mutation VendorComplianceTabUploadReportMutation(\n $input: UploadVendorComplianceReportInput!\n) {\n uploadVendorComplianceReport(input: $input) {\n vendorComplianceReportEdge {\n node {\n id\n ...VendorComplianceTabFragment_report\n }\n }\n }\n}\n\nfragment VendorComplianceTabFragment_report on VendorComplianceReport {\n id\n reportDate\n validUntil\n reportName\n file {\n fileName\n mimeType\n size\n id\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -28,20 +28,17 @@ import (
|
||||
|
||||
type (
|
||||
Evidence struct {
|
||||
ID gid.GID `db:"id"`
|
||||
MeasureID gid.GID `db:"measure_id"`
|
||||
TaskID *gid.GID `db:"task_id"`
|
||||
State EvidenceState `db:"state"`
|
||||
ReferenceID string `db:"reference_id"`
|
||||
Type EvidenceType `db:"type"`
|
||||
ObjectKey string `db:"object_key"`
|
||||
MimeType string `db:"mime_type"`
|
||||
Size uint64 `db:"size"`
|
||||
Filename string `db:"filename"`
|
||||
URL string `db:"url"`
|
||||
Description string `db:"description"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
ID gid.GID `db:"id"`
|
||||
MeasureID gid.GID `db:"measure_id"`
|
||||
TaskID *gid.GID `db:"task_id"`
|
||||
State EvidenceState `db:"state"`
|
||||
ReferenceID string `db:"reference_id"`
|
||||
Type EvidenceType `db:"type"`
|
||||
URL string `db:"url"`
|
||||
EvidenceFileId *gid.GID `db:"evidence_file_id"`
|
||||
Description string `db:"description"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
}
|
||||
|
||||
Evidences []*Evidence
|
||||
@@ -69,13 +66,10 @@ INSERT INTO
|
||||
measure_id,
|
||||
task_id,
|
||||
reference_id,
|
||||
object_key,
|
||||
mime_type,
|
||||
size,
|
||||
state,
|
||||
type,
|
||||
filename,
|
||||
url,
|
||||
evidence_file_id,
|
||||
description,
|
||||
created_at,
|
||||
updated_at
|
||||
@@ -86,13 +80,10 @@ VALUES (
|
||||
@measure_id,
|
||||
@task_id,
|
||||
@reference_id,
|
||||
@object_key,
|
||||
@mime_type,
|
||||
@size,
|
||||
@state,
|
||||
@type,
|
||||
@filename,
|
||||
@url,
|
||||
@evidence_file_id,
|
||||
@description,
|
||||
@created_at,
|
||||
@updated_at
|
||||
@@ -105,21 +96,18 @@ WHERE evidences.state = 'REQUESTED';
|
||||
`
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"evidence_id": e.ID,
|
||||
"measure_id": e.MeasureID,
|
||||
"task_id": e.TaskID,
|
||||
"reference_id": e.ReferenceID,
|
||||
"object_key": e.ObjectKey,
|
||||
"mime_type": e.MimeType,
|
||||
"size": e.Size,
|
||||
"filename": e.Filename,
|
||||
"created_at": e.CreatedAt,
|
||||
"updated_at": e.UpdatedAt,
|
||||
"state": e.State,
|
||||
"type": e.Type,
|
||||
"url": e.URL,
|
||||
"description": e.Description,
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"evidence_id": e.ID,
|
||||
"measure_id": e.MeasureID,
|
||||
"task_id": e.TaskID,
|
||||
"reference_id": e.ReferenceID,
|
||||
"evidence_file_id": e.EvidenceFileId,
|
||||
"created_at": e.CreatedAt,
|
||||
"updated_at": e.UpdatedAt,
|
||||
"state": e.State,
|
||||
"type": e.Type,
|
||||
"url": e.URL,
|
||||
"description": e.Description,
|
||||
}
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
return err
|
||||
@@ -138,13 +126,10 @@ INSERT INTO
|
||||
measure_id,
|
||||
task_id,
|
||||
reference_id,
|
||||
object_key,
|
||||
mime_type,
|
||||
size,
|
||||
state,
|
||||
type,
|
||||
filename,
|
||||
url,
|
||||
evidence_file_id,
|
||||
description,
|
||||
created_at,
|
||||
updated_at
|
||||
@@ -155,13 +140,10 @@ VALUES (
|
||||
@measure_id,
|
||||
@task_id,
|
||||
@reference_id,
|
||||
@object_key,
|
||||
@mime_type,
|
||||
@size,
|
||||
@state,
|
||||
@type,
|
||||
@filename,
|
||||
@url,
|
||||
@evidence_file_id,
|
||||
@description,
|
||||
@created_at,
|
||||
@updated_at
|
||||
@@ -169,21 +151,18 @@ VALUES (
|
||||
`
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"evidence_id": e.ID,
|
||||
"measure_id": e.MeasureID,
|
||||
"task_id": e.TaskID,
|
||||
"reference_id": e.ReferenceID,
|
||||
"object_key": e.ObjectKey,
|
||||
"mime_type": e.MimeType,
|
||||
"size": e.Size,
|
||||
"filename": e.Filename,
|
||||
"created_at": e.CreatedAt,
|
||||
"updated_at": e.UpdatedAt,
|
||||
"state": e.State,
|
||||
"type": e.Type,
|
||||
"url": e.URL,
|
||||
"description": e.Description,
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"evidence_id": e.ID,
|
||||
"measure_id": e.MeasureID,
|
||||
"task_id": e.TaskID,
|
||||
"reference_id": e.ReferenceID,
|
||||
"evidence_file_id": e.EvidenceFileId,
|
||||
"created_at": e.CreatedAt,
|
||||
"updated_at": e.UpdatedAt,
|
||||
"state": e.State,
|
||||
"type": e.Type,
|
||||
"url": e.URL,
|
||||
"description": e.Description,
|
||||
}
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
return err
|
||||
@@ -203,11 +182,8 @@ SELECT
|
||||
reference_id,
|
||||
state,
|
||||
type,
|
||||
object_key,
|
||||
mime_type,
|
||||
size,
|
||||
filename,
|
||||
url,
|
||||
evidence_file_id,
|
||||
description,
|
||||
created_at,
|
||||
updated_at
|
||||
@@ -286,11 +262,8 @@ SELECT
|
||||
reference_id,
|
||||
state,
|
||||
type,
|
||||
object_key,
|
||||
mime_type,
|
||||
size,
|
||||
filename,
|
||||
url,
|
||||
evidence_file_id,
|
||||
description,
|
||||
created_at,
|
||||
updated_at
|
||||
@@ -370,11 +343,8 @@ SELECT
|
||||
reference_id,
|
||||
state,
|
||||
type,
|
||||
object_key,
|
||||
mime_type,
|
||||
size,
|
||||
filename,
|
||||
url,
|
||||
evidence_file_id,
|
||||
description,
|
||||
created_at,
|
||||
updated_at
|
||||
@@ -418,10 +388,7 @@ UPDATE
|
||||
SET
|
||||
type = @type,
|
||||
state = @state,
|
||||
object_key = @object_key,
|
||||
mime_type = @mime_type,
|
||||
size = @size,
|
||||
filename = @filename,
|
||||
evidence_file_id = @evidence_file_id,
|
||||
url = @url,
|
||||
description = @description,
|
||||
updated_at = @updated_at
|
||||
@@ -433,16 +400,13 @@ WHERE
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"evidence_id": e.ID,
|
||||
"type": e.Type,
|
||||
"state": e.State,
|
||||
"object_key": e.ObjectKey,
|
||||
"mime_type": e.MimeType,
|
||||
"size": e.Size,
|
||||
"filename": e.Filename,
|
||||
"url": e.URL,
|
||||
"description": e.Description,
|
||||
"updated_at": e.UpdatedAt,
|
||||
"evidence_id": e.ID,
|
||||
"type": e.Type,
|
||||
"state": e.State,
|
||||
"evidence_file_id": e.EvidenceFileId,
|
||||
"url": e.URL,
|
||||
"description": e.Description,
|
||||
"updated_at": e.UpdatedAt,
|
||||
}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
@@ -454,13 +418,14 @@ func (e Evidence) Delete(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
) error {
|
||||
) (*string, error) {
|
||||
q := `
|
||||
DELETE FROM
|
||||
evidences
|
||||
WHERE
|
||||
%s
|
||||
%s
|
||||
AND id = @evidence_id
|
||||
RETURNING evidence_file_id;
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
@@ -468,6 +433,23 @@ WHERE
|
||||
args := pgx.StrictNamedArgs{"evidence_id": e.ID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
return err
|
||||
var evidenceFileId *gid.GID
|
||||
err := conn.QueryRow(ctx, q, args).Scan(&evidenceFileId)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to delete evidence: %w", err)
|
||||
}
|
||||
|
||||
if evidenceFileId != nil {
|
||||
var err error
|
||||
var fileKey *string
|
||||
file := &File{ID: *evidenceFileId}
|
||||
if fileKey, err = file.HardDelete(ctx, conn, scope); err != nil {
|
||||
return nil, fmt.Errorf("failed to hard delete evidence file: %w", err)
|
||||
}
|
||||
return fileKey, nil
|
||||
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -135,3 +135,39 @@ VALUES (
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
return err
|
||||
}
|
||||
|
||||
func (f File) SoftDelete(ctx context.Context, conn pg.Conn, scope Scoper) error {
|
||||
q := `
|
||||
UPDATE files
|
||||
SET deleted_at = NOW()
|
||||
WHERE %s
|
||||
AND id = @file_id
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{"file_id": f.ID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (f File) HardDelete(ctx context.Context, conn pg.Conn, scope Scoper) (*string, error) {
|
||||
q := `
|
||||
DELETE FROM files
|
||||
WHERE %s
|
||||
AND id = @file_id
|
||||
RETURNING file_key
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
args := pgx.StrictNamedArgs{"file_id": f.ID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
var vcrFileKey *string
|
||||
err := conn.QueryRow(ctx, q, args).Scan(&vcrFileKey)
|
||||
|
||||
return vcrFileKey, err
|
||||
}
|
||||
|
||||
41
pkg/coredata/migrations/20251003T132724Z.sql
Normal file
41
pkg/coredata/migrations/20251003T132724Z.sql
Normal file
@@ -0,0 +1,41 @@
|
||||
ALTER TABLE evidences
|
||||
ADD COLUMN evidence_file_id text;
|
||||
|
||||
/* 10 is for FileEntityType */
|
||||
WITH
|
||||
evidence_files AS (
|
||||
SELECT
|
||||
e.id as evidence_id,
|
||||
generate_gid(decode_base64_unpadded(e.tenant_id), 25) as file_id,
|
||||
e.tenant_id,
|
||||
'probod' as bucket_name,
|
||||
e.mime_type,
|
||||
e.filename,
|
||||
e.object_key,
|
||||
e.size,
|
||||
e.created_at,
|
||||
e.updated_at
|
||||
FROM evidences e
|
||||
),
|
||||
inserted_files AS (
|
||||
INSERT INTO files (id, tenant_id, bucket_name, mime_type, file_name, file_key, file_size, created_at, updated_at)
|
||||
SELECT file_id, tenant_id, bucket_name, mime_type, filename, object_key::uuid, size, created_at, updated_at
|
||||
FROM evidence_files
|
||||
RETURNING id, tenant_id
|
||||
)
|
||||
|
||||
SELECT ef.evidence_id, ef.file_id
|
||||
INTO TEMP TABLE file_evidence_mapping
|
||||
FROM evidence_files ef;
|
||||
|
||||
UPDATE evidences
|
||||
SET evidence_file_id = fm.file_id
|
||||
FROM file_evidence_mapping fm
|
||||
WHERE evidences.id = fm.evidence_id;
|
||||
|
||||
|
||||
ALTER TABLE evidences
|
||||
ALTER COLUMN filename DROP NOT NULL,
|
||||
ALTER COLUMN mime_type DROP NOT NULL,
|
||||
ALTER COLUMN size DROP NOT NULL,
|
||||
ALTER COLUMN oject_key DROP NOT NULL;
|
||||
41
pkg/coredata/migrations/20251003T151644Z.sql
Normal file
41
pkg/coredata/migrations/20251003T151644Z.sql
Normal file
@@ -0,0 +1,41 @@
|
||||
ALTER TABLE
|
||||
vendor_compliance_reports
|
||||
ADD COLUMN report_file_id text
|
||||
REFERENCES files(id)
|
||||
ON UPDATE CASCADE
|
||||
ON DELETE RESTRICT;
|
||||
|
||||
WITH
|
||||
/* 25 is for FileEntityType */
|
||||
vcr_files AS (
|
||||
SELECT
|
||||
vcr.id as report_id,
|
||||
generate_gid(decode_base64_unpadded(vcr.tenant_id), 25) as file_id,
|
||||
vcr.tenant_id,
|
||||
'probod' as bucket_name,
|
||||
'application/pdf' as mime_type,
|
||||
vcr.report_name,
|
||||
vcr.file_key,
|
||||
vcr.file_size,
|
||||
vcr.created_at,
|
||||
vcr.updated_at
|
||||
FROM vendor_compliance_reports vcr
|
||||
),
|
||||
inserted_files AS (
|
||||
INSERT INTO files (id, tenant_id, bucket_name, mime_type, file_name, file_key, file_size, created_at, updated_at)
|
||||
SELECT file_id, tenant_id, bucket_name, mime_type, report_name, file_key::uuid, file_size, created_at, updated_at
|
||||
FROM vcr_files
|
||||
RETURNING id, tenant_id
|
||||
)
|
||||
SELECT vf.report_id, vf.file_id
|
||||
INTO TEMP TABLE file_vcr_mapping
|
||||
FROM vcr_files vf;
|
||||
|
||||
UPDATE vendor_compliance_reports
|
||||
SET report_file_id = fv.file_id
|
||||
FROM file_vcr_mapping fv
|
||||
WHERE vendor_compliance_reports.id = fv.report_id;
|
||||
|
||||
ALTER TABLE vendor_compliance_reports
|
||||
ALTER COLUMN file_key DROP NOT NULL,
|
||||
ALTER COLUMN file_size DROP NOT NULL;
|
||||
7
pkg/coredata/migrations/20251006T133729Z.sql
Normal file
7
pkg/coredata/migrations/20251006T133729Z.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
ALTER TABLE
|
||||
evidences
|
||||
ADD CONSTRAINT fk_evidence_file
|
||||
FOREIGN KEY (evidence_file_id)
|
||||
REFERENCES files(id)
|
||||
ON UPDATE CASCADE
|
||||
ON DELETE RESTRICT;
|
||||
@@ -28,17 +28,16 @@ import (
|
||||
|
||||
type (
|
||||
VendorComplianceReport struct {
|
||||
ID gid.GID
|
||||
VendorID gid.GID
|
||||
ReportDate time.Time
|
||||
ValidUntil *time.Time
|
||||
ReportName string
|
||||
FileKey string
|
||||
FileSize int64
|
||||
SnapshotID *gid.GID
|
||||
SourceID *gid.GID
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
ID gid.GID
|
||||
VendorID gid.GID
|
||||
ReportDate time.Time
|
||||
ValidUntil *time.Time
|
||||
ReportName string
|
||||
ReportFileId *gid.GID
|
||||
SnapshotID *gid.GID
|
||||
SourceID *gid.GID
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
VendorComplianceReports []*VendorComplianceReport
|
||||
@@ -69,8 +68,7 @@ SELECT
|
||||
report_date,
|
||||
valid_until,
|
||||
report_name,
|
||||
file_key,
|
||||
file_size,
|
||||
report_file_id,
|
||||
snapshot_id,
|
||||
source_id,
|
||||
created_at,
|
||||
@@ -117,8 +115,7 @@ SELECT
|
||||
report_date,
|
||||
valid_until,
|
||||
report_name,
|
||||
file_key,
|
||||
file_size,
|
||||
report_file_id,
|
||||
snapshot_id,
|
||||
source_id,
|
||||
created_at,
|
||||
@@ -165,8 +162,7 @@ INSERT INTO
|
||||
report_date,
|
||||
valid_until,
|
||||
report_name,
|
||||
file_key,
|
||||
file_size,
|
||||
report_file_id,
|
||||
created_at,
|
||||
updated_at
|
||||
)
|
||||
@@ -177,23 +173,21 @@ VALUES (
|
||||
@report_date,
|
||||
@valid_until,
|
||||
@report_name,
|
||||
@file_key,
|
||||
@file_size,
|
||||
@report_file_id,
|
||||
@created_at,
|
||||
@updated_at
|
||||
)
|
||||
`
|
||||
args := pgx.NamedArgs{
|
||||
"id": vcr.ID,
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"vendor_id": vcr.VendorID,
|
||||
"report_date": vcr.ReportDate,
|
||||
"valid_until": vcr.ValidUntil,
|
||||
"report_name": vcr.ReportName,
|
||||
"file_key": vcr.FileKey,
|
||||
"file_size": vcr.FileSize,
|
||||
"created_at": vcr.CreatedAt,
|
||||
"updated_at": vcr.UpdatedAt,
|
||||
"id": vcr.ID,
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"vendor_id": vcr.VendorID,
|
||||
"report_date": vcr.ReportDate,
|
||||
"valid_until": vcr.ValidUntil,
|
||||
"report_name": vcr.ReportName,
|
||||
"report_file_id": vcr.ReportFileId,
|
||||
"created_at": vcr.CreatedAt,
|
||||
"updated_at": vcr.UpdatedAt,
|
||||
}
|
||||
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
@@ -213,6 +207,7 @@ WHERE
|
||||
%s
|
||||
AND id = @id
|
||||
AND snapshot_id IS NULL
|
||||
RETURNING report_file_id
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
@@ -220,8 +215,20 @@ WHERE
|
||||
args := pgx.StrictNamedArgs{"id": vcr.ID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
return err
|
||||
var vcrFileId *gid.GID
|
||||
err := conn.QueryRow(ctx, q, args).Scan(&vcrFileId)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete vendor compliance report: %w", err)
|
||||
}
|
||||
|
||||
if vcrFileId != nil {
|
||||
file := &File{ID: *vcrFileId}
|
||||
if err = file.SoftDelete(ctx, conn, scope); err != nil {
|
||||
return fmt.Errorf("failed to soft delete vendor compliance file: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vcrs VendorComplianceReports) InsertVendorSnapshots(
|
||||
@@ -247,8 +254,7 @@ INSERT INTO vendor_compliance_reports (
|
||||
report_date,
|
||||
valid_until,
|
||||
report_name,
|
||||
file_key,
|
||||
file_size,
|
||||
report_file_id,
|
||||
created_at,
|
||||
updated_at
|
||||
)
|
||||
@@ -261,8 +267,7 @@ SELECT
|
||||
vcr.report_date,
|
||||
vcr.valid_until,
|
||||
vcr.report_name,
|
||||
vcr.file_key,
|
||||
vcr.file_size,
|
||||
vcr.report_file_id,
|
||||
vcr.created_at,
|
||||
vcr.updated_at
|
||||
FROM vendor_compliance_reports vcr
|
||||
|
||||
@@ -65,6 +65,7 @@ var FileTypes = []FileType{
|
||||
{MimeType: "text/plain", Extensions: []string{".txt"}, Category: CategoryText},
|
||||
{MimeType: "text/x-log", Extensions: []string{".log"}, Category: CategoryText},
|
||||
{MimeType: "text/uri-list", Extensions: []string{".uri"}, Category: CategoryText},
|
||||
{MimeType: "text/uri-list; charset=utf-8", Extensions: []string{".uri"}, Category: CategoryText},
|
||||
|
||||
// Image types
|
||||
{MimeType: "image/jpeg", Extensions: []string{".jpg", ".jpeg"}, Category: CategoryImage},
|
||||
|
||||
@@ -15,17 +15,10 @@
|
||||
package probo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/getprobo/probo/pkg/coredata"
|
||||
"github.com/getprobo/probo/pkg/filevalidation"
|
||||
"github.com/getprobo/probo/pkg/gid"
|
||||
@@ -40,36 +33,10 @@ type (
|
||||
fileValidator *filevalidation.FileValidator
|
||||
}
|
||||
|
||||
File struct {
|
||||
Content io.Reader
|
||||
Filename string
|
||||
Size int64
|
||||
ContentType string
|
||||
}
|
||||
|
||||
RequestEvidenceRequest struct {
|
||||
MeasureID *gid.GID
|
||||
TaskID *gid.GID
|
||||
Type coredata.EvidenceType
|
||||
Name string
|
||||
Description string
|
||||
}
|
||||
|
||||
FulfilledEvidenceRequest struct {
|
||||
EvidenceID gid.GID
|
||||
File io.Reader
|
||||
URL *string
|
||||
Filename *string
|
||||
}
|
||||
|
||||
UploadTaskEvidenceRequest struct {
|
||||
TaskID gid.GID
|
||||
File File
|
||||
}
|
||||
|
||||
UploadMeasureEvidenceRequest struct {
|
||||
MeasureID gid.GID
|
||||
File File
|
||||
URL *string
|
||||
File FileUpload
|
||||
}
|
||||
)
|
||||
|
||||
@@ -82,256 +49,8 @@ func (s EvidenceService) Get(
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
return evidence.LoadByID(ctx, conn, s.svc.scope, evidenceID)
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return evidence, nil
|
||||
}
|
||||
|
||||
func (s EvidenceService) Request(
|
||||
ctx context.Context,
|
||||
req RequestEvidenceRequest,
|
||||
) (*coredata.Evidence, error) {
|
||||
evidenceID := gid.New(s.svc.scope.GetTenantID(), coredata.EvidenceEntityType)
|
||||
now := time.Now()
|
||||
|
||||
evidence := &coredata.Evidence{
|
||||
ID: evidenceID,
|
||||
State: coredata.EvidenceStateRequested,
|
||||
Type: req.Type,
|
||||
Filename: req.Name,
|
||||
Description: req.Description,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
err := s.svc.pg.WithTx(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
task := &coredata.Task{}
|
||||
if req.TaskID != nil {
|
||||
if err := task.LoadByID(ctx, conn, s.svc.scope, *req.TaskID); err != nil {
|
||||
return fmt.Errorf("cannot load task: %w", err)
|
||||
}
|
||||
|
||||
if task.MeasureID == nil {
|
||||
return fmt.Errorf("task %q has no measure", req.TaskID)
|
||||
}
|
||||
|
||||
evidence.TaskID = req.TaskID
|
||||
evidence.MeasureID = *task.MeasureID
|
||||
} else if req.MeasureID != nil {
|
||||
evidence.MeasureID = *req.MeasureID
|
||||
} else {
|
||||
return fmt.Errorf("measure id or task id is required")
|
||||
}
|
||||
|
||||
return evidence.Insert(ctx, conn, s.svc.scope)
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot insert evidence: %w", err)
|
||||
}
|
||||
|
||||
return evidence, nil
|
||||
}
|
||||
|
||||
func (s EvidenceService) Fulfill(
|
||||
ctx context.Context,
|
||||
req FulfilledEvidenceRequest,
|
||||
) (*coredata.Evidence, error) {
|
||||
evidence := &coredata.Evidence{}
|
||||
|
||||
err := s.svc.pg.WithTx(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
if err := evidence.LoadByID(ctx, conn, s.svc.scope, req.EvidenceID); err != nil {
|
||||
return fmt.Errorf("cannot load evidence: %w", err)
|
||||
}
|
||||
|
||||
measure := &coredata.Measure{}
|
||||
if err := measure.LoadByID(ctx, conn, s.svc.scope, evidence.MeasureID); err != nil {
|
||||
return fmt.Errorf("cannot load measure: %w", err)
|
||||
}
|
||||
|
||||
evidence.State = coredata.EvidenceStateFulfilled
|
||||
|
||||
if req.File != nil {
|
||||
evidence.Type = coredata.EvidenceTypeFile
|
||||
|
||||
var fileSize int64
|
||||
var fileContent io.ReadSeeker
|
||||
|
||||
if seeker, ok := req.File.(io.Seeker); ok {
|
||||
size, err := seeker.Seek(0, io.SeekEnd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot determine file size: %w", err)
|
||||
}
|
||||
|
||||
_, err = seeker.Seek(0, io.SeekStart)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot reset file position: %w", err)
|
||||
}
|
||||
fileSize = size
|
||||
fileContent = req.File.(io.ReadSeeker)
|
||||
} else {
|
||||
buf, err := io.ReadAll(req.File)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot read file: %w", err)
|
||||
}
|
||||
fileSize = int64(len(buf))
|
||||
fileContent = bytes.NewReader(buf)
|
||||
}
|
||||
|
||||
contentType := "application/octet-stream"
|
||||
if req.Filename != nil {
|
||||
evidence.Filename = *req.Filename
|
||||
if detectedType := mime.TypeByExtension(filepath.Ext(*req.Filename)); detectedType != "" {
|
||||
contentType = detectedType
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.fileValidator.Validate(evidence.Filename, contentType, fileSize); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
objectKey, err := uuid.NewV7()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot generate object key: %w", err)
|
||||
}
|
||||
|
||||
_, err = s.svc.s3.PutObject(ctx, &s3.PutObjectInput{
|
||||
Bucket: aws.String(s.svc.bucket),
|
||||
Key: aws.String(objectKey.String()),
|
||||
Body: fileContent,
|
||||
ContentType: aws.String(contentType),
|
||||
Metadata: map[string]string{
|
||||
"type": "evidence",
|
||||
"evidence-id": req.EvidenceID.String(),
|
||||
"organization-id": measure.OrganizationID.String(),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot upload file to S3: %w", err)
|
||||
}
|
||||
|
||||
headOutput, err := s.svc.s3.HeadObject(ctx, &s3.HeadObjectInput{
|
||||
Bucket: aws.String(s.svc.bucket),
|
||||
Key: aws.String(objectKey.String()),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot get object metadata: %w", err)
|
||||
}
|
||||
|
||||
evidence.ObjectKey = objectKey.String()
|
||||
evidence.MimeType = contentType
|
||||
evidence.Size = uint64(*headOutput.ContentLength)
|
||||
} else if req.URL != nil {
|
||||
evidence.Type = coredata.EvidenceTypeLink
|
||||
evidence.URL = *req.URL
|
||||
}
|
||||
|
||||
evidence.UpdatedAt = time.Now()
|
||||
|
||||
return evidence.Update(ctx, conn, s.svc.scope)
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot update evidence: %w", err)
|
||||
}
|
||||
|
||||
return evidence, nil
|
||||
}
|
||||
|
||||
func (s EvidenceService) UploadTaskEvidence(
|
||||
ctx context.Context,
|
||||
req UploadTaskEvidenceRequest,
|
||||
) (*coredata.Evidence, error) {
|
||||
now := time.Now()
|
||||
evidenceID := gid.New(s.svc.scope.GetTenantID(), coredata.EvidenceEntityType)
|
||||
|
||||
referenceID, err := uuid.NewV4()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot generate reference id: %w", err)
|
||||
}
|
||||
|
||||
evidence := &coredata.Evidence{
|
||||
ID: evidenceID,
|
||||
TaskID: &req.TaskID,
|
||||
State: coredata.EvidenceStateFulfilled,
|
||||
ReferenceID: "custom-evidence-" + referenceID.String(),
|
||||
Type: coredata.EvidenceTypeFile,
|
||||
Filename: req.File.Filename,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
if req.File.ContentType == "" {
|
||||
req.File.ContentType = "application/octet-stream"
|
||||
}
|
||||
|
||||
if err := s.fileValidator.Validate(req.File.Filename, req.File.ContentType, req.File.Size); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var measureOrganizationID gid.GID
|
||||
|
||||
err = s.svc.pg.WithTx(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
task := &coredata.Task{}
|
||||
|
||||
if err := task.LoadByID(ctx, conn, s.svc.scope, req.TaskID); err != nil {
|
||||
return fmt.Errorf("cannot load task %q: %w", req.TaskID, err)
|
||||
}
|
||||
|
||||
if task.MeasureID == nil {
|
||||
return fmt.Errorf("task %q has no measure", req.TaskID)
|
||||
}
|
||||
|
||||
measure := &coredata.Measure{}
|
||||
if err := measure.LoadByID(ctx, conn, s.svc.scope, *task.MeasureID); err != nil {
|
||||
return fmt.Errorf("cannot load measure: %w", err)
|
||||
}
|
||||
measureOrganizationID = measure.OrganizationID
|
||||
|
||||
objectKey, err := uuid.NewV7()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot generate object key: %w", err)
|
||||
}
|
||||
|
||||
_, err = s.svc.s3.PutObject(
|
||||
ctx,
|
||||
&s3.PutObjectInput{
|
||||
Bucket: aws.String(s.svc.bucket),
|
||||
Key: aws.String(objectKey.String()),
|
||||
Body: req.File.Content,
|
||||
ContentType: aws.String(req.File.ContentType),
|
||||
Metadata: map[string]string{
|
||||
"type": "evidence",
|
||||
"evidence-id": evidenceID.String(),
|
||||
"organization-id": measureOrganizationID.String(),
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot upload file to S3: %w", err)
|
||||
}
|
||||
|
||||
evidence.ObjectKey = objectKey.String()
|
||||
evidence.MimeType = req.File.ContentType
|
||||
evidence.Size = uint64(req.File.Size)
|
||||
evidence.MeasureID = *task.MeasureID
|
||||
|
||||
if err := evidence.Insert(ctx, conn, s.svc.scope); err != nil {
|
||||
return fmt.Errorf("cannot insert evidence: %w", err)
|
||||
if err := evidence.LoadByID(ctx, conn, s.svc.scope, evidenceID); err != nil {
|
||||
return fmt.Errorf("cannot load evidence %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -339,8 +58,7 @@ func (s EvidenceService) UploadTaskEvidence(
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
// TODO try do delete file from s3 if it's a file type
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("cannot load evidence: %w", err)
|
||||
}
|
||||
|
||||
return evidence, nil
|
||||
@@ -364,53 +82,37 @@ func (s EvidenceService) UploadMeasureEvidence(
|
||||
State: coredata.EvidenceStateFulfilled,
|
||||
ReferenceID: "custom-evidence-" + referenceID.String(),
|
||||
Type: coredata.EvidenceTypeFile,
|
||||
Filename: req.File.Filename,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
if req.File.ContentType == "" {
|
||||
req.File.ContentType = "application/octet-stream"
|
||||
}
|
||||
|
||||
if err := s.fileValidator.Validate(req.File.Filename, req.File.ContentType, req.File.Size); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = s.svc.pg.WithTx(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
measure := &coredata.Measure{}
|
||||
var file *coredata.File
|
||||
var err error
|
||||
|
||||
file, err = s.svc.Files.UploadAndSaveFile(
|
||||
ctx,
|
||||
s.fileValidator,
|
||||
map[string]string{
|
||||
"type": "evidence",
|
||||
"evidence-id": evidenceID.String(),
|
||||
"organization-id": measure.OrganizationID.String(),
|
||||
},
|
||||
&req.File)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot upload or file: %w", err)
|
||||
}
|
||||
|
||||
evidence.EvidenceFileId = &file.ID
|
||||
|
||||
if err := measure.LoadByID(ctx, conn, s.svc.scope, req.MeasureID); err != nil {
|
||||
return fmt.Errorf("cannot load measure %q: %w", req.MeasureID, err)
|
||||
}
|
||||
|
||||
objectKey, err := uuid.NewV7()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot generate object key: %w", err)
|
||||
}
|
||||
|
||||
_, err = s.svc.s3.PutObject(
|
||||
ctx,
|
||||
&s3.PutObjectInput{
|
||||
Bucket: aws.String(s.svc.bucket),
|
||||
Key: aws.String(objectKey.String()),
|
||||
Body: req.File.Content,
|
||||
ContentType: aws.String(req.File.ContentType),
|
||||
Metadata: map[string]string{
|
||||
"type": "evidence",
|
||||
"evidence-id": evidenceID.String(),
|
||||
"organization-id": measure.OrganizationID.String(),
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot upload file to S3: %w", err)
|
||||
}
|
||||
|
||||
evidence.ObjectKey = objectKey.String()
|
||||
evidence.MimeType = req.File.ContentType
|
||||
evidence.Size = uint64(req.File.Size)
|
||||
evidence.MeasureID = req.MeasureID
|
||||
|
||||
if err := evidence.Insert(ctx, conn, s.svc.scope); err != nil {
|
||||
@@ -429,43 +131,6 @@ func (s EvidenceService) UploadMeasureEvidence(
|
||||
return evidence, nil
|
||||
}
|
||||
|
||||
func (s EvidenceService) GenerateFileURL(
|
||||
ctx context.Context,
|
||||
evidenceID gid.GID,
|
||||
expiresIn time.Duration,
|
||||
) (*string, error) {
|
||||
evidence, err := s.Get(ctx, evidenceID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot get evidence: %w", err)
|
||||
}
|
||||
|
||||
if evidence.Type == coredata.EvidenceTypeLink {
|
||||
return nil, fmt.Errorf("cannot generate file URL for link type evidence")
|
||||
}
|
||||
|
||||
presignClient := s3.NewPresignClient(s.svc.s3)
|
||||
|
||||
// Use RFC 6266/5987 encoding for filename with UTF-8 support
|
||||
encodedFilename := url.QueryEscape(evidence.Filename)
|
||||
contentDisposition := fmt.Sprintf("attachment; filename=\"%s\"; filename*=UTF-8''%s",
|
||||
encodedFilename, encodedFilename)
|
||||
|
||||
presignedReq, err := presignClient.PresignGetObject(ctx, &s3.GetObjectInput{
|
||||
Bucket: aws.String(s.svc.bucket),
|
||||
Key: aws.String(evidence.ObjectKey),
|
||||
ResponseCacheControl: aws.String("max-age=3600, public"),
|
||||
ResponseContentType: aws.String(evidence.MimeType),
|
||||
ResponseContentDisposition: aws.String(contentDisposition),
|
||||
}, func(opts *s3.PresignOptions) {
|
||||
opts.Expires = expiresIn
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot presign GetObject request: %w", err)
|
||||
}
|
||||
|
||||
return &presignedReq.URL, nil
|
||||
}
|
||||
|
||||
func (s EvidenceService) CountForMeasureID(
|
||||
ctx context.Context,
|
||||
measureID gid.GID,
|
||||
@@ -581,9 +246,15 @@ func (s *EvidenceService) Delete(
|
||||
return s.svc.pg.WithTx(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
if err := evidence.Delete(ctx, conn, s.svc.scope); err != nil {
|
||||
var fileKey *string
|
||||
var err error
|
||||
|
||||
if fileKey, err = evidence.Delete(ctx, conn, s.svc.scope); err != nil {
|
||||
return fmt.Errorf("cannot delete evidence: %w", err)
|
||||
}
|
||||
if err = s.svc.Files.DeleteFileFromS3(ctx, *fileKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
|
||||
187
pkg/probo/file_service.go
Normal file
187
pkg/probo/file_service.go
Normal file
@@ -0,0 +1,187 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package probo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/getprobo/probo/pkg/coredata"
|
||||
"github.com/getprobo/probo/pkg/filevalidation"
|
||||
"github.com/getprobo/probo/pkg/gid"
|
||||
"go.gearno.de/crypto/uuid"
|
||||
"go.gearno.de/kit/pg"
|
||||
)
|
||||
|
||||
type (
|
||||
FileService struct {
|
||||
svc *TenantService
|
||||
}
|
||||
|
||||
File struct {
|
||||
Content io.Reader
|
||||
Filename string
|
||||
Size int64
|
||||
ContentType string
|
||||
}
|
||||
|
||||
FileUpload struct {
|
||||
Content io.Reader
|
||||
Filename string
|
||||
Size int64
|
||||
ContentType string
|
||||
}
|
||||
)
|
||||
|
||||
func (s FileService) Get(
|
||||
ctx context.Context,
|
||||
fileID gid.GID,
|
||||
) (*coredata.File, error) {
|
||||
file := &coredata.File{}
|
||||
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
if err := file.LoadByID(ctx, conn, s.svc.scope, fileID); err != nil {
|
||||
return fmt.Errorf("cannot load file %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot load file: %w", err)
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func (s FileService) UploadAndSaveFile(
|
||||
ctx context.Context,
|
||||
fileValidator *filevalidation.FileValidator,
|
||||
s3Metadata map[string]string,
|
||||
req *FileUpload) (*coredata.File, error) {
|
||||
objectKey, err := uuid.NewV7()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot generate object key: %w", err)
|
||||
}
|
||||
|
||||
mimeType := req.ContentType
|
||||
if mimeType == "" {
|
||||
mimeType = "application/octet-stream"
|
||||
}
|
||||
|
||||
if err := fileValidator.Validate(req.Filename, mimeType, req.Size); err != nil {
|
||||
return nil, fmt.Errorf("cannot validate file: %w", err)
|
||||
}
|
||||
|
||||
_, err = s.svc.s3.PutObject(ctx, &s3.PutObjectInput{
|
||||
Bucket: &s.svc.bucket,
|
||||
Key: aws.String(objectKey.String()),
|
||||
Body: req.Content,
|
||||
Metadata: s3Metadata,
|
||||
ContentType: aws.String(mimeType),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot upload file to S3: %w", err)
|
||||
}
|
||||
|
||||
headOutput, err := s.svc.s3.HeadObject(ctx, &s3.HeadObjectInput{
|
||||
Bucket: aws.String(s.svc.bucket),
|
||||
Key: aws.String(objectKey.String()),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot get object metadata: %w", err)
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
fileID := gid.New(s.svc.scope.GetTenantID(), coredata.FileEntityType)
|
||||
var file *coredata.File
|
||||
|
||||
err = s.svc.pg.WithTx(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
|
||||
file = &coredata.File{
|
||||
ID: fileID,
|
||||
BucketName: s.svc.bucket,
|
||||
MimeType: mimeType,
|
||||
FileName: req.Filename,
|
||||
FileKey: objectKey.String(),
|
||||
FileSize: *headOutput.ContentLength,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
if err := file.Insert(ctx, conn, s.svc.scope); err != nil {
|
||||
return fmt.Errorf("cannot insert file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func (s FileService) DeleteFileFromS3(ctx context.Context, fileKey string) error {
|
||||
_, err := s.svc.s3.DeleteObject(ctx, &s3.DeleteObjectInput{
|
||||
Bucket: aws.String(s.svc.bucket),
|
||||
Key: aws.String(fileKey),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete object from S3: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s FileService) GenerateFileTempURL(
|
||||
ctx context.Context,
|
||||
fileID gid.GID,
|
||||
expiresIn time.Duration,
|
||||
) (string, error) {
|
||||
file, err := s.Get(ctx, fileID)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot get file: %w", err)
|
||||
}
|
||||
|
||||
presignClient := s3.NewPresignClient(s.svc.s3)
|
||||
|
||||
presignedReq, err := presignClient.PresignGetObject(ctx, &s3.GetObjectInput{
|
||||
Bucket: aws.String(s.svc.bucket),
|
||||
Key: aws.String(file.FileKey),
|
||||
ResponseCacheControl: aws.String("max-age=3600, public"),
|
||||
ResponseContentType: aws.String(file.MimeType),
|
||||
ResponseContentDisposition: aws.String(fmt.Sprintf("attachment; filename=\"%s\"", file.FileName)),
|
||||
}, func(opts *s3.PresignOptions) {
|
||||
opts.Expires = expiresIn
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot presign GetObject request: %w", err)
|
||||
}
|
||||
|
||||
return presignedReq.URL, nil
|
||||
}
|
||||
@@ -223,15 +223,20 @@ func (s FrameworkService) Export(
|
||||
for _, evidence := range evidences {
|
||||
if evidence.Type != coredata.EvidenceTypeFile ||
|
||||
evidence.State != coredata.EvidenceStateFulfilled ||
|
||||
evidence.ObjectKey == "" {
|
||||
evidence.EvidenceFileId == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
evidence_file := &coredata.File{}
|
||||
if err := evidence_file.LoadByID(ctx, conn, s.svc.scope, *evidence.EvidenceFileId); err != nil {
|
||||
return fmt.Errorf("cannot load evidence file: %w", err)
|
||||
}
|
||||
|
||||
object, err := s.svc.s3.GetObject(
|
||||
ctx,
|
||||
&s3.GetObjectInput{
|
||||
Bucket: aws.String(s.svc.bucket),
|
||||
Key: aws.String(evidence.ObjectKey),
|
||||
Key: aws.String(evidence_file.FileKey),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
@@ -239,7 +244,7 @@ func (s FrameworkService) Export(
|
||||
}
|
||||
defer object.Body.Close()
|
||||
|
||||
w, err := archive.Create(fmt.Sprintf("%s/%s/%s/%s", framework.Name, control.SectionTitle, measure.Name, evidence.Filename))
|
||||
w, err := archive.Create(fmt.Sprintf("%s/%s/%s/%s", framework.Name, control.SectionTitle, measure.Name, evidence_file.FileName))
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot create evidence in archive: %w", err)
|
||||
}
|
||||
|
||||
@@ -94,6 +94,7 @@ type (
|
||||
Snapshots *SnapshotService
|
||||
ContinualImprovements *ContinualImprovementService
|
||||
ProcessingActivities *ProcessingActivityService
|
||||
Files *FileService
|
||||
}
|
||||
)
|
||||
|
||||
@@ -175,7 +176,7 @@ func (s *Service) WithTenant(tenantID gid.TenantID) *TenantService {
|
||||
}
|
||||
tenantService.Controls = &ControlService{svc: tenantService}
|
||||
tenantService.Risks = &RiskService{svc: tenantService}
|
||||
tenantService.VendorComplianceReports = &VendorComplianceReportService{svc: tenantService}
|
||||
tenantService.VendorComplianceReports = &VendorComplianceReportService{svc: tenantService, fileValidator: filevalidation.NewValidator(filevalidation.CategoryDocument)}
|
||||
tenantService.VendorBusinessAssociateAgreements = &VendorBusinessAssociateAgreementService{svc: tenantService}
|
||||
tenantService.VendorContacts = &VendorContactService{svc: tenantService}
|
||||
tenantService.VendorDataPrivacyAgreements = &VendorDataPrivacyAgreementService{svc: tenantService}
|
||||
@@ -193,6 +194,7 @@ func (s *Service) WithTenant(tenantID gid.TenantID) *TenantService {
|
||||
tenantService.Snapshots = &SnapshotService{svc: tenantService}
|
||||
tenantService.ContinualImprovements = &ContinualImprovementService{svc: tenantService}
|
||||
tenantService.ProcessingActivities = &ProcessingActivityService{svc: tenantService}
|
||||
tenantService.Files = &FileService{svc: tenantService}
|
||||
return tenantService
|
||||
}
|
||||
|
||||
|
||||
@@ -17,28 +17,23 @@ package probo
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/getprobo/probo/pkg/coredata"
|
||||
"github.com/getprobo/probo/pkg/filevalidation"
|
||||
"github.com/getprobo/probo/pkg/gid"
|
||||
"github.com/getprobo/probo/pkg/page"
|
||||
"go.gearno.de/crypto/uuid"
|
||||
"go.gearno.de/kit/pg"
|
||||
)
|
||||
|
||||
type (
|
||||
VendorComplianceReportService struct {
|
||||
svc *TenantService
|
||||
svc *TenantService
|
||||
fileValidator *filevalidation.FileValidator
|
||||
}
|
||||
|
||||
VendorComplianceReportCreateRequest struct {
|
||||
File io.Reader
|
||||
File FileUpload
|
||||
ReportDate time.Time
|
||||
ValidUntil *time.Time
|
||||
ReportName string
|
||||
@@ -71,61 +66,43 @@ func (s VendorComplianceReportService) Upload(
|
||||
vendorID gid.GID,
|
||||
req *VendorComplianceReportCreateRequest,
|
||||
) (*coredata.VendorComplianceReport, error) {
|
||||
objectKey, err := uuid.NewV7()
|
||||
vendor, err := s.svc.Vendors.Get(ctx, vendorID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot generate object key: %w", err)
|
||||
return nil, fmt.Errorf("cannot get vendor: %w", err)
|
||||
}
|
||||
|
||||
var vendorComplianceReport *coredata.VendorComplianceReport
|
||||
f, err := s.svc.Files.UploadAndSaveFile(
|
||||
ctx,
|
||||
s.fileValidator,
|
||||
map[string]string{
|
||||
"type": "vendor-compliance-report",
|
||||
"vendor-id": vendorID.String(),
|
||||
"organization-id": vendor.OrganizationID.String(),
|
||||
},
|
||||
&req.File)
|
||||
|
||||
err = s.svc.pg.WithTx(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
vendorComplianceReportID := gid.New(s.svc.scope.GetTenantID(), coredata.VendorComplianceReportEntityType)
|
||||
|
||||
vendorComplianceReport := &coredata.VendorComplianceReport{
|
||||
ID: vendorComplianceReportID,
|
||||
VendorID: vendorID,
|
||||
ReportDate: req.ReportDate,
|
||||
ValidUntil: req.ValidUntil,
|
||||
ReportName: req.ReportName,
|
||||
ReportFileId: &f.ID,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
err = s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
vendor := &coredata.Vendor{}
|
||||
if err := vendor.LoadByID(ctx, conn, s.svc.scope, vendorID); err != nil {
|
||||
return fmt.Errorf("cannot load vendor: %w", err)
|
||||
}
|
||||
|
||||
mimeType := mime.TypeByExtension(filepath.Ext(req.ReportName))
|
||||
|
||||
_, err := s.svc.s3.PutObject(ctx, &s3.PutObjectInput{
|
||||
Bucket: &s.svc.bucket,
|
||||
Key: aws.String(objectKey.String()),
|
||||
Body: req.File,
|
||||
ContentType: &mimeType,
|
||||
Metadata: map[string]string{
|
||||
"type": "vendor-compliance-report",
|
||||
"vendor-id": vendorID.String(),
|
||||
"organization-id": vendor.OrganizationID.String(),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot upload file to S3: %w", err)
|
||||
}
|
||||
|
||||
headOutput, err := s.svc.s3.HeadObject(ctx, &s3.HeadObjectInput{
|
||||
Bucket: aws.String(s.svc.bucket),
|
||||
Key: aws.String(objectKey.String()),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot get object metadata: %w", err)
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
vendorComplianceReportID := gid.New(s.svc.scope.GetTenantID(), coredata.VendorComplianceReportEntityType)
|
||||
|
||||
vendorComplianceReport = &coredata.VendorComplianceReport{
|
||||
ID: vendorComplianceReportID,
|
||||
VendorID: vendorID,
|
||||
ReportDate: req.ReportDate,
|
||||
ValidUntil: req.ValidUntil,
|
||||
ReportName: req.ReportName,
|
||||
FileKey: objectKey.String(),
|
||||
FileSize: *headOutput.ContentLength,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
return vendorComplianceReport.Insert(ctx, conn, s.svc.scope)
|
||||
},
|
||||
)
|
||||
@@ -157,38 +134,6 @@ func (s VendorComplianceReportService) Get(
|
||||
return vendorComplianceReport, nil
|
||||
}
|
||||
|
||||
func (s VendorComplianceReportService) GenerateFileURL(
|
||||
ctx context.Context,
|
||||
vendorComplianceReportID gid.GID,
|
||||
expiresIn time.Duration,
|
||||
) (string, error) {
|
||||
vendorComplianceReport, err := s.Get(ctx, vendorComplianceReportID)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot get vendor compliance report: %w", err)
|
||||
}
|
||||
|
||||
presignClient := s3.NewPresignClient(s.svc.s3)
|
||||
|
||||
// Use RFC 6266/5987 encoding for filename with UTF-8 support
|
||||
encodedFilename := url.QueryEscape(vendorComplianceReport.ReportName)
|
||||
contentDisposition := fmt.Sprintf("attachment; filename=\"%s\"; filename*=UTF-8''%s",
|
||||
encodedFilename, encodedFilename)
|
||||
|
||||
presignedReq, err := presignClient.PresignGetObject(ctx, &s3.GetObjectInput{
|
||||
Bucket: aws.String(s.svc.bucket),
|
||||
Key: aws.String(vendorComplianceReport.FileKey),
|
||||
ResponseCacheControl: aws.String("max-age=3600, public"),
|
||||
ResponseContentDisposition: aws.String(contentDisposition),
|
||||
}, func(opts *s3.PresignOptions) {
|
||||
opts.Expires = expiresIn
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot presign GetObject request: %w", err)
|
||||
}
|
||||
|
||||
return presignedReq.URL, nil
|
||||
}
|
||||
|
||||
func (s VendorComplianceReportService) Delete(
|
||||
ctx context.Context,
|
||||
vendorComplianceReportID gid.GID,
|
||||
@@ -198,7 +143,11 @@ func (s VendorComplianceReportService) Delete(
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
return vendorComplianceReport.Delete(ctx, conn, s.svc.scope)
|
||||
if err := vendorComplianceReport.Delete(ctx, conn, s.svc.scope); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -1755,8 +1755,7 @@ type VendorComplianceReport implements Node {
|
||||
reportDate: Datetime!
|
||||
validUntil: Datetime
|
||||
reportName: String!
|
||||
fileUrl: String! @goField(forceResolver: true)
|
||||
fileSize: BigInt!
|
||||
file: File @goField(forceResolver: true)
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
@@ -1944,12 +1943,10 @@ type Task implements Node {
|
||||
|
||||
type Evidence implements Node {
|
||||
id: ID!
|
||||
fileUrl: String @goField(forceResolver: true)
|
||||
mimeType: String!
|
||||
size: Int!
|
||||
state: EvidenceState!
|
||||
type: EvidenceType!
|
||||
filename: String!
|
||||
file: File @goField(forceResolver: true)
|
||||
url: String
|
||||
description: String!
|
||||
|
||||
@@ -2590,6 +2587,16 @@ type SnapshotEdge {
|
||||
node: Snapshot!
|
||||
}
|
||||
|
||||
type File {
|
||||
id: ID!
|
||||
mimeType: String!
|
||||
fileName: String!
|
||||
size: BigInt!
|
||||
downloadUrl: String! @goField(forceResolver: true)
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
# Root Types
|
||||
type Query {
|
||||
node(id: ID!): Node!
|
||||
@@ -2751,12 +2758,7 @@ type Mutation {
|
||||
): DeleteRiskObligationMappingPayload!
|
||||
|
||||
# Evidence mutations
|
||||
requestEvidence(input: RequestEvidenceInput!): RequestEvidencePayload!
|
||||
fulfillEvidence(input: FulfillEvidenceInput!): FulfillEvidencePayload!
|
||||
deleteEvidence(input: DeleteEvidenceInput!): DeleteEvidencePayload!
|
||||
uploadTaskEvidence(
|
||||
input: UploadTaskEvidenceInput!
|
||||
): UploadTaskEvidencePayload!
|
||||
uploadMeasureEvidence(
|
||||
input: UploadMeasureEvidenceInput!
|
||||
): UploadMeasureEvidencePayload!
|
||||
@@ -4142,19 +4144,10 @@ type CancelSignatureRequestPayload {
|
||||
deletedDocumentVersionSignatureId: ID!
|
||||
}
|
||||
|
||||
type UploadTaskEvidencePayload {
|
||||
evidenceEdge: EvidenceEdge!
|
||||
}
|
||||
|
||||
type UploadMeasureEvidencePayload {
|
||||
evidenceEdge: EvidenceEdge!
|
||||
}
|
||||
|
||||
input UploadTaskEvidenceInput {
|
||||
taskId: ID!
|
||||
file: Upload!
|
||||
}
|
||||
|
||||
input UploadMeasureEvidenceInput {
|
||||
measureId: ID!
|
||||
file: Upload!
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -61,8 +61,6 @@ func NewEvidenceEdge(e *coredata.Evidence, orderBy coredata.EvidenceOrderField)
|
||||
}
|
||||
|
||||
func NewEvidence(e *coredata.Evidence) *Evidence {
|
||||
var fileURL *string = nil
|
||||
|
||||
var urlPtr *string = nil
|
||||
if e.URL != "" {
|
||||
urlCopy := e.URL
|
||||
@@ -73,10 +71,6 @@ func NewEvidence(e *coredata.Evidence) *Evidence {
|
||||
ID: e.ID,
|
||||
State: e.State,
|
||||
Type: e.Type,
|
||||
FileURL: fileURL,
|
||||
Filename: e.Filename,
|
||||
MimeType: e.MimeType,
|
||||
Size: int(e.Size),
|
||||
URL: urlPtr,
|
||||
Description: e.Description,
|
||||
CreatedAt: e.CreatedAt,
|
||||
|
||||
30
pkg/server/api/console/v1/types/file.go
Normal file
30
pkg/server/api/console/v1/types/file.go
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/getprobo/probo/pkg/coredata"
|
||||
)
|
||||
|
||||
func NewFile(r *coredata.File) *File {
|
||||
return &File{
|
||||
ID: r.ID,
|
||||
MimeType: r.MimeType,
|
||||
FileName: r.FileName,
|
||||
Size: r.FileSize,
|
||||
CreatedAt: r.CreatedAt,
|
||||
UpdatedAt: r.UpdatedAt,
|
||||
}
|
||||
}
|
||||
@@ -1034,12 +1034,10 @@ type DocumentVersionSignatureOrder struct {
|
||||
|
||||
type Evidence struct {
|
||||
ID gid.GID `json:"id"`
|
||||
FileURL *string `json:"fileUrl,omitempty"`
|
||||
MimeType string `json:"mimeType"`
|
||||
Size int `json:"size"`
|
||||
State coredata.EvidenceState `json:"state"`
|
||||
Type coredata.EvidenceType `json:"type"`
|
||||
Filename string `json:"filename"`
|
||||
File *File `json:"file,omitempty"`
|
||||
URL *string `json:"url,omitempty"`
|
||||
Description string `json:"description"`
|
||||
Task *Task `json:"task,omitempty"`
|
||||
@@ -1075,6 +1073,16 @@ type ExportFrameworkPayload struct {
|
||||
ExportJobID gid.GID `json:"exportJobId"`
|
||||
}
|
||||
|
||||
type File struct {
|
||||
ID gid.GID `json:"id"`
|
||||
MimeType string `json:"mimeType"`
|
||||
FileName string `json:"fileName"`
|
||||
Size int64 `json:"size"`
|
||||
DownloadURL string `json:"downloadUrl"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type Framework struct {
|
||||
ID gid.GID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
@@ -1955,15 +1963,6 @@ type UploadMeasureEvidencePayload struct {
|
||||
EvidenceEdge *EvidenceEdge `json:"evidenceEdge"`
|
||||
}
|
||||
|
||||
type UploadTaskEvidenceInput struct {
|
||||
TaskID gid.GID `json:"taskId"`
|
||||
File graphql.Upload `json:"file"`
|
||||
}
|
||||
|
||||
type UploadTaskEvidencePayload struct {
|
||||
EvidenceEdge *EvidenceEdge `json:"evidenceEdge"`
|
||||
}
|
||||
|
||||
type UploadTrustCenterNDAInput struct {
|
||||
TrustCenterID gid.GID `json:"trustCenterId"`
|
||||
FileName string `json:"fileName"`
|
||||
@@ -2090,8 +2089,7 @@ type VendorComplianceReport struct {
|
||||
ReportDate time.Time `json:"reportDate"`
|
||||
ValidUntil *time.Time `json:"validUntil,omitempty"`
|
||||
ReportName string `json:"reportName"`
|
||||
FileURL string `json:"fileUrl"`
|
||||
FileSize int64 `json:"fileSize"`
|
||||
File *File `json:"file,omitempty"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
@@ -49,7 +49,6 @@ func NewVendorComplianceReport(c *coredata.VendorComplianceReport) *VendorCompli
|
||||
ReportDate: c.ReportDate,
|
||||
ValidUntil: c.ValidUntil,
|
||||
ReportName: c.ReportName,
|
||||
FileSize: c.FileSize,
|
||||
CreatedAt: c.CreatedAt,
|
||||
UpdatedAt: c.UpdatedAt,
|
||||
}
|
||||
|
||||
@@ -734,21 +734,25 @@ func (r *documentVersionSignatureResolver) SignedBy(ctx context.Context, obj *ty
|
||||
return types.NewPeople(people), nil
|
||||
}
|
||||
|
||||
// FileURL is the resolver for the fileUrl field.
|
||||
func (r *evidenceResolver) FileURL(ctx context.Context, obj *types.Evidence) (*string, error) {
|
||||
// File is the resolver for the file field.
|
||||
func (r *evidenceResolver) File(ctx context.Context, obj *types.Evidence) (*types.File, error) {
|
||||
prb := r.ProboService(ctx, obj.ID.TenantID())
|
||||
|
||||
if obj.Type == coredata.EvidenceTypeLink {
|
||||
return obj.URL, nil
|
||||
}
|
||||
|
||||
fileURL, err := prb.Evidences.GenerateFileURL(ctx, obj.ID, 15*time.Minute)
|
||||
evidence, err := prb.Evidences.Get(ctx, obj.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot generate file URL: %w", err)
|
||||
return nil, fmt.Errorf("cannot load evidence: %w", err)
|
||||
}
|
||||
|
||||
result := *fileURL
|
||||
return &result, nil
|
||||
if evidence.EvidenceFileId == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
file, err := prb.Files.Get(ctx, *evidence.EvidenceFileId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot load evidence file: %w", err)
|
||||
}
|
||||
|
||||
return types.NewFile(file), nil
|
||||
}
|
||||
|
||||
// Task is the resolver for the task field.
|
||||
@@ -811,6 +815,18 @@ func (r *evidenceConnectionResolver) TotalCount(ctx context.Context, obj *types.
|
||||
panic(fmt.Errorf("unsupported resolver: %T", obj.Resolver))
|
||||
}
|
||||
|
||||
// DownloadURL is the resolver for the downloadUrl field.
|
||||
func (r *fileResolver) DownloadURL(ctx context.Context, obj *types.File) (string, error) {
|
||||
prb := r.ProboService(ctx, obj.ID.TenantID())
|
||||
|
||||
downloadUrl, err := prb.Files.GenerateFileTempURL(ctx, obj.ID, 60*time.Second)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot generate download url: %w", err)
|
||||
}
|
||||
|
||||
return downloadUrl, nil
|
||||
}
|
||||
|
||||
// Organization is the resolver for the organization field.
|
||||
func (r *frameworkResolver) Organization(ctx context.Context, obj *types.Framework) (*types.Organization, error) {
|
||||
prb := r.ProboService(ctx, obj.ID.TenantID())
|
||||
@@ -2227,55 +2243,6 @@ func (r *mutationResolver) DeleteRiskObligationMapping(ctx context.Context, inpu
|
||||
}, nil
|
||||
}
|
||||
|
||||
// RequestEvidence is the resolver for the requestEvidence field.
|
||||
func (r *mutationResolver) RequestEvidence(ctx context.Context, input types.RequestEvidenceInput) (*types.RequestEvidencePayload, error) {
|
||||
prb := r.ProboService(ctx, input.TaskID.TenantID())
|
||||
|
||||
evidence, err := prb.Evidences.Request(
|
||||
ctx,
|
||||
probo.RequestEvidenceRequest{
|
||||
TaskID: &input.TaskID,
|
||||
Name: input.Name,
|
||||
Type: input.Type,
|
||||
Description: input.Description,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot request evidence: %w", err))
|
||||
}
|
||||
|
||||
return &types.RequestEvidencePayload{
|
||||
EvidenceEdge: types.NewEvidenceEdge(evidence, coredata.EvidenceOrderFieldCreatedAt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// FulfillEvidence is the resolver for the fulfillEvidence field.
|
||||
func (r *mutationResolver) FulfillEvidence(ctx context.Context, input types.FulfillEvidenceInput) (*types.FulfillEvidencePayload, error) {
|
||||
prb := r.ProboService(ctx, input.EvidenceID.TenantID())
|
||||
|
||||
req := probo.FulfilledEvidenceRequest{
|
||||
EvidenceID: input.EvidenceID,
|
||||
}
|
||||
|
||||
if input.File != nil {
|
||||
req.File = input.File.File
|
||||
req.Filename = &input.File.Filename
|
||||
}
|
||||
|
||||
if input.URL != nil {
|
||||
req.URL = input.URL
|
||||
}
|
||||
|
||||
evidence, err := prb.Evidences.Fulfill(ctx, req)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot fulfill evidence: %w", err))
|
||||
}
|
||||
|
||||
return &types.FulfillEvidencePayload{
|
||||
EvidenceEdge: types.NewEvidenceEdge(evidence, coredata.EvidenceOrderFieldCreatedAt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteEvidence is the resolver for the deleteEvidence field.
|
||||
func (r *mutationResolver) DeleteEvidence(ctx context.Context, input types.DeleteEvidenceInput) (*types.DeleteEvidencePayload, error) {
|
||||
prb := r.ProboService(ctx, input.EvidenceID.TenantID())
|
||||
@@ -2290,31 +2257,6 @@ func (r *mutationResolver) DeleteEvidence(ctx context.Context, input types.Delet
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UploadTaskEvidence is the resolver for the uploadTaskEvidence field.
|
||||
func (r *mutationResolver) UploadTaskEvidence(ctx context.Context, input types.UploadTaskEvidenceInput) (*types.UploadTaskEvidencePayload, error) {
|
||||
prb := r.ProboService(ctx, input.TaskID.TenantID())
|
||||
|
||||
evidence, err := prb.Evidences.UploadTaskEvidence(
|
||||
ctx,
|
||||
probo.UploadTaskEvidenceRequest{
|
||||
TaskID: input.TaskID,
|
||||
File: probo.File{
|
||||
Content: input.File.File,
|
||||
Filename: input.File.Filename,
|
||||
Size: input.File.Size,
|
||||
ContentType: input.File.ContentType,
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot upload task evidence: %w", err))
|
||||
}
|
||||
|
||||
return &types.UploadTaskEvidencePayload{
|
||||
EvidenceEdge: types.NewEvidenceEdge(evidence, coredata.EvidenceOrderFieldCreatedAt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// UploadMeasureEvidence is the resolver for the uploadMeasureEvidence field.
|
||||
func (r *mutationResolver) UploadMeasureEvidence(ctx context.Context, input types.UploadMeasureEvidenceInput) (*types.UploadMeasureEvidencePayload, error) {
|
||||
prb := r.ProboService(ctx, input.MeasureID.TenantID())
|
||||
@@ -2323,7 +2265,7 @@ func (r *mutationResolver) UploadMeasureEvidence(ctx context.Context, input type
|
||||
ctx,
|
||||
probo.UploadMeasureEvidenceRequest{
|
||||
MeasureID: input.MeasureID,
|
||||
File: probo.File{
|
||||
File: probo.FileUpload{
|
||||
Content: input.File.File,
|
||||
Filename: input.File.Filename,
|
||||
Size: input.File.Size,
|
||||
@@ -2348,7 +2290,7 @@ func (r *mutationResolver) UploadVendorComplianceReport(ctx context.Context, inp
|
||||
ctx,
|
||||
input.VendorID,
|
||||
&probo.VendorComplianceReportCreateRequest{
|
||||
File: input.File.File,
|
||||
File: probo.FileUpload{Filename: input.File.Filename, Size: input.File.Size, Content: input.File.File, ContentType: input.File.ContentType},
|
||||
ReportDate: input.ReportDate,
|
||||
ValidUntil: input.ValidUntil,
|
||||
ReportName: input.ReportName,
|
||||
@@ -5065,16 +5007,25 @@ func (r *vendorComplianceReportResolver) Vendor(ctx context.Context, obj *types.
|
||||
return types.NewVendor(vendor), nil
|
||||
}
|
||||
|
||||
// FileURL is the resolver for the fileUrl field.
|
||||
func (r *vendorComplianceReportResolver) FileURL(ctx context.Context, obj *types.VendorComplianceReport) (string, error) {
|
||||
// File is the resolver for the file field.
|
||||
func (r *vendorComplianceReportResolver) File(ctx context.Context, obj *types.VendorComplianceReport) (*types.File, error) {
|
||||
prb := r.ProboService(ctx, obj.ID.TenantID())
|
||||
|
||||
fileURL, err := prb.VendorComplianceReports.GenerateFileURL(ctx, obj.ID, 1*time.Hour)
|
||||
evidence, err := prb.VendorComplianceReports.Get(ctx, obj.ID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to generate file URL: %w", err))
|
||||
return nil, fmt.Errorf("cannot load evidence: %w", err)
|
||||
}
|
||||
|
||||
return fileURL, nil
|
||||
if evidence.ReportFileId == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
file, err := prb.Files.Get(ctx, *evidence.ReportFileId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot load evidence file: %w", err)
|
||||
}
|
||||
|
||||
return types.NewFile(file), nil
|
||||
}
|
||||
|
||||
// TotalCount is the resolver for the totalCount field.
|
||||
@@ -5271,6 +5222,9 @@ func (r *Resolver) EvidenceConnection() schema.EvidenceConnectionResolver {
|
||||
return &evidenceConnectionResolver{r}
|
||||
}
|
||||
|
||||
// File returns schema.FileResolver implementation.
|
||||
func (r *Resolver) File() schema.FileResolver { return &fileResolver{r} }
|
||||
|
||||
// Framework returns schema.FrameworkResolver implementation.
|
||||
func (r *Resolver) Framework() schema.FrameworkResolver { return &frameworkResolver{r} }
|
||||
|
||||
@@ -5434,6 +5388,7 @@ type documentVersionResolver struct{ *Resolver }
|
||||
type documentVersionSignatureResolver struct{ *Resolver }
|
||||
type evidenceResolver struct{ *Resolver }
|
||||
type evidenceConnectionResolver struct{ *Resolver }
|
||||
type fileResolver struct{ *Resolver }
|
||||
type frameworkResolver struct{ *Resolver }
|
||||
type frameworkConnectionResolver struct{ *Resolver }
|
||||
type measureResolver struct{ *Resolver }
|
||||
|
||||
Reference in New Issue
Block a user