Switch console file fields to File download URLs
Replace presigned URL string fields (logoUrl, fileUrl, ndaFileName, etc.) with nested File references resolved through /api/files/v1/. Update console Relay queries and e2e coverage accordingly. Route NDA upload through filemanager.PutFile and return stable IAM org logo URLs for consistency with the files API. Signed-off-by: Ludovic Vielle <ludovic@probo.com>
This commit is contained in:
@@ -53,8 +53,12 @@ export const auditNodeQuery = graphql`
|
||||
framework {
|
||||
id
|
||||
name
|
||||
lightLogoURL
|
||||
darkLogoURL
|
||||
lightLogo {
|
||||
downloadUrl
|
||||
}
|
||||
darkLogo {
|
||||
downloadUrl
|
||||
}
|
||||
}
|
||||
organization {
|
||||
id
|
||||
|
||||
@@ -47,7 +47,10 @@ const uploadTrustCenterNDAMutation = graphql`
|
||||
uploadTrustCenterNDA(input: $input) {
|
||||
trustCenter {
|
||||
id
|
||||
ndaFileName
|
||||
nda {
|
||||
fileName
|
||||
downloadUrl
|
||||
}
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
@@ -68,7 +71,10 @@ const deleteTrustCenterNDAMutation = graphql`
|
||||
deleteTrustCenterNDA(input: $input) {
|
||||
trustCenter {
|
||||
id
|
||||
ndaFileName
|
||||
nda {
|
||||
fileName
|
||||
downloadUrl
|
||||
}
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,9 @@ export const createTrustCenterReferenceMutation = graphql`
|
||||
name
|
||||
description
|
||||
websiteUrl
|
||||
logoUrl
|
||||
logo {
|
||||
downloadUrl
|
||||
}
|
||||
rank
|
||||
createdAt
|
||||
updatedAt
|
||||
@@ -55,7 +57,9 @@ export const updateTrustCenterReferenceMutation = graphql`
|
||||
name
|
||||
description
|
||||
websiteUrl
|
||||
logoUrl
|
||||
logo {
|
||||
downloadUrl
|
||||
}
|
||||
rank
|
||||
createdAt
|
||||
updatedAt
|
||||
|
||||
@@ -189,8 +189,8 @@ export default function AuditDetailsPage(props: Props) {
|
||||
<div className="flex items-center gap-3">
|
||||
<FrameworkLogo
|
||||
name={auditEntry.framework?.name || ""}
|
||||
lightLogoURL={auditEntry.framework?.lightLogoURL}
|
||||
darkLogoURL={auditEntry.framework?.darkLogoURL}
|
||||
lightLogoURL={auditEntry.framework?.lightLogo?.downloadUrl}
|
||||
darkLogoURL={auditEntry.framework?.darkLogo?.downloadUrl}
|
||||
/>
|
||||
<div className="text-2xl">{auditEntry.framework?.name}</div>
|
||||
</div>
|
||||
|
||||
@@ -26,7 +26,10 @@ const fragment = graphql`
|
||||
fragment ElectronicSignatureSectionFragment on ElectronicSignature {
|
||||
status
|
||||
signedAt
|
||||
certificateFileUrl
|
||||
certificate {
|
||||
downloadUrl
|
||||
fileName
|
||||
}
|
||||
events {
|
||||
id
|
||||
eventType
|
||||
@@ -36,16 +39,6 @@ const fragment = graphql`
|
||||
}
|
||||
`;
|
||||
|
||||
function getFilenameFromUrl(url: string): string | null {
|
||||
try {
|
||||
const disposition = new URL(url).searchParams.get("response-content-disposition");
|
||||
const match = disposition?.match(/filename="([^"]+)"/);
|
||||
return match ? decodeURIComponent(match[1]) : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function ElectronicSignatureSection({
|
||||
fragmentRef,
|
||||
}: {
|
||||
@@ -72,17 +65,17 @@ export function ElectronicSignatureSection({
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{signature.certificateFileUrl && (
|
||||
{signature.certificate?.downloadUrl && (
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-txt-secondary">{__("Certificate")}</span>
|
||||
<a
|
||||
href={signature.certificateFileUrl}
|
||||
href={signature.certificate.downloadUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm text-txt-primary hover:underline"
|
||||
download
|
||||
>
|
||||
{getFilenameFromUrl(signature.certificateFileUrl) ?? __("Download")}
|
||||
{signature.certificate.fileName ?? __("Download")}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -42,8 +42,12 @@ export const compliancePageBrandPageQuery = graphql`
|
||||
... on Organization {
|
||||
compliancePage: trustCenter @required(action: THROW) {
|
||||
id
|
||||
logoFileUrl
|
||||
darkLogoFileUrl
|
||||
logo {
|
||||
downloadUrl
|
||||
}
|
||||
darkLogo {
|
||||
downloadUrl
|
||||
}
|
||||
canUpdate: permission(action: "core:trust-center:update")
|
||||
...CompliancePageFrameworkList_compliancePageFragment
|
||||
...CompliancePageExternalUrlsSection_trustCenterFragment
|
||||
@@ -58,8 +62,12 @@ const updateTrustCenterBrandMutation = graphql`
|
||||
updateTrustCenterBrand(input: $input) {
|
||||
trustCenter {
|
||||
id
|
||||
logoFileUrl
|
||||
darkLogoFileUrl
|
||||
logo {
|
||||
downloadUrl
|
||||
}
|
||||
darkLogo {
|
||||
downloadUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,8 +85,8 @@ export function CompliancePageBrandPage(props: { queryRef: PreloadedQuery<Compli
|
||||
}
|
||||
|
||||
const trustCenterId = organization.compliancePage.id;
|
||||
const logoFileUrl = organization.compliancePage.logoFileUrl;
|
||||
const darkLogoFileUrl = organization.compliancePage.darkLogoFileUrl;
|
||||
const logoDownloadUrl = organization.compliancePage.logo?.downloadUrl;
|
||||
const darkLogoDownloadUrl = organization.compliancePage.darkLogo?.downloadUrl;
|
||||
|
||||
const [logoPreview, setLogoPreview] = useState<string | null>(null);
|
||||
const [darkLogoPreview, setDarkLogoPreview] = useState<string | null>(null);
|
||||
@@ -234,8 +242,8 @@ export function CompliancePageBrandPage(props: { queryRef: PreloadedQuery<Compli
|
||||
});
|
||||
};
|
||||
|
||||
const currentLogoUrl = logoPreview || logoFileUrl;
|
||||
const currentDarkLogoUrl = darkLogoPreview || darkLogoFileUrl;
|
||||
const currentLogoUrl = logoPreview || logoDownloadUrl;
|
||||
const currentDarkLogoUrl = darkLogoPreview || darkLogoDownloadUrl;
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
|
||||
@@ -35,7 +35,9 @@ const fileFragment = graphql`
|
||||
id
|
||||
name
|
||||
category
|
||||
fileUrl
|
||||
file {
|
||||
downloadUrl
|
||||
}
|
||||
trustCenterVisibility
|
||||
createdAt
|
||||
canUpdate: permission(action: "core:trust-center-file:update")
|
||||
@@ -124,7 +126,7 @@ export function CompliancePageFileListItem(props: {
|
||||
variant="secondary"
|
||||
icon={IconArrowLink}
|
||||
onClick={() =>
|
||||
window.open(file.fileUrl, "_blank", "noopener,noreferrer")}
|
||||
window.open(file.file?.downloadUrl, "_blank", "noopener,noreferrer")}
|
||||
title={__("Download")}
|
||||
/>
|
||||
{file.canUpdate && (
|
||||
|
||||
@@ -45,8 +45,12 @@ const compliancePageFragment = graphql`
|
||||
framework {
|
||||
id
|
||||
name
|
||||
lightLogoURL
|
||||
darkLogoURL
|
||||
lightLogo {
|
||||
downloadUrl
|
||||
}
|
||||
darkLogo {
|
||||
downloadUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,8 +202,8 @@ function CompliancePageFrameworkListItem(props: {
|
||||
<div className="flex items-center gap-3">
|
||||
<FrameworkLogo
|
||||
className="size-8"
|
||||
lightLogoURL={framework.lightLogoURL}
|
||||
darkLogoURL={framework.darkLogoURL}
|
||||
lightLogoURL={framework.lightLogo?.downloadUrl}
|
||||
darkLogoURL={framework.darkLogo?.downloadUrl}
|
||||
name={framework.name}
|
||||
/>
|
||||
{framework.name}
|
||||
|
||||
@@ -24,8 +24,10 @@ const fragment = graphql`
|
||||
fragment CompliancePageNDASectionFragment on Organization {
|
||||
compliancePage: trustCenter {
|
||||
id
|
||||
ndaFileName
|
||||
ndaFileUrl
|
||||
nda {
|
||||
fileName
|
||||
downloadUrl
|
||||
}
|
||||
canUploadNDA: permission(action: "core:trust-center:upload-nda")
|
||||
canDeleteNDA: permission(action: "core:trust-center:delete-nda")
|
||||
}
|
||||
@@ -103,7 +105,7 @@ export function CompliancePageNDASection(props: { fragmentRef: CompliancePageNDA
|
||||
{(isUploadingNDA || isDeletingNDA) && <Spinner />}
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
{!organization.compliancePage?.ndaFileName
|
||||
{!organization.compliancePage?.nda?.fileName
|
||||
&& organization.compliancePage?.canUploadNDA
|
||||
? (
|
||||
<p className="text-sm text-txt-tertiary">
|
||||
@@ -115,14 +117,14 @@ export function CompliancePageNDASection(props: { fragmentRef: CompliancePageNDA
|
||||
: (
|
||||
<></>
|
||||
)}
|
||||
{organization.compliancePage?.ndaFileName
|
||||
{organization.compliancePage?.nda?.fileName
|
||||
? (
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<p className="text-sm font-medium">
|
||||
{organization.compliancePage.ndaFileName
|
||||
{organization.compliancePage.nda?.fileName
|
||||
|| __("Non-Disclosure Agreement")}
|
||||
</p>
|
||||
</div>
|
||||
@@ -137,9 +139,9 @@ export function CompliancePageNDASection(props: { fragmentRef: CompliancePageNDA
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
if (organization.compliancePage?.ndaFileUrl) {
|
||||
if (organization.compliancePage?.nda?.downloadUrl) {
|
||||
window.open(
|
||||
organization.compliancePage.ndaFileUrl,
|
||||
organization.compliancePage.nda.downloadUrl,
|
||||
"_blank",
|
||||
"noopener,noreferrer",
|
||||
);
|
||||
|
||||
@@ -24,7 +24,9 @@ import { DeleteTrustCenterReferenceDialog } from "#/components/trustCenter/Delet
|
||||
const fragment = graphql`
|
||||
fragment CompliancePageReferenceListItemFragment on TrustCenterReference {
|
||||
id
|
||||
logoUrl
|
||||
logo {
|
||||
downloadUrl
|
||||
}
|
||||
name
|
||||
description
|
||||
websiteUrl
|
||||
@@ -81,7 +83,7 @@ export function CompliancePageReferenceListItem(props: {
|
||||
>
|
||||
<Td>
|
||||
<div className="flex items-center gap-3">
|
||||
<Avatar src={reference.logoUrl} name={reference.name} size="m" />
|
||||
<Avatar src={reference.logo?.downloadUrl} name={reference.name} size="m" />
|
||||
<span className="font-medium">{reference.name}</span>
|
||||
</div>
|
||||
</Td>
|
||||
|
||||
@@ -53,10 +53,13 @@ const frameworkDetailFragment = graphql`
|
||||
name
|
||||
# eslint-disable-next-line relay/unused-fields
|
||||
description
|
||||
lightLogo {
|
||||
downloadUrl
|
||||
}
|
||||
# eslint-disable-next-line relay/unused-fields
|
||||
lightLogoURL
|
||||
# eslint-disable-next-line relay/unused-fields
|
||||
darkLogoURL
|
||||
darkLogo {
|
||||
downloadUrl
|
||||
}
|
||||
canExport: permission(action: "core:franework:export")
|
||||
canUpdate: permission(action: "core:framework:update")
|
||||
canDelete: permission(action: "core:framework:delete")
|
||||
@@ -147,7 +150,11 @@ export default function FrameworkDetailPage(props: Props) {
|
||||
<PageHeader
|
||||
title={(
|
||||
<>
|
||||
<FrameworkLogo {...framework} />
|
||||
<FrameworkLogo
|
||||
name={framework.name}
|
||||
lightLogoURL={framework.lightLogo?.downloadUrl}
|
||||
darkLogoURL={framework.darkLogo?.downloadUrl}
|
||||
/>
|
||||
{framework.name}
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -187,8 +187,12 @@ const frameworkCardFragment = graphql`
|
||||
id
|
||||
name
|
||||
description
|
||||
lightLogoURL
|
||||
darkLogoURL
|
||||
lightLogo {
|
||||
downloadUrl
|
||||
}
|
||||
darkLogo {
|
||||
downloadUrl
|
||||
}
|
||||
canUpdate: permission(action: "core:framework:update")
|
||||
canDelete: permission(action: "core:framework:delete")
|
||||
}
|
||||
@@ -220,8 +224,8 @@ function FrameworkCard(props: FrameworkCardProps) {
|
||||
<div className="flex justify-between mb-3">
|
||||
<FrameworkLogo
|
||||
name={framework.name}
|
||||
lightLogoURL={framework.lightLogoURL}
|
||||
darkLogoURL={framework.darkLogoURL}
|
||||
lightLogoURL={framework.lightLogo?.downloadUrl}
|
||||
darkLogoURL={framework.darkLogo?.downloadUrl}
|
||||
/>
|
||||
{props.hasAnyAction && (
|
||||
<ActionDropdown className="z-10 relative">
|
||||
|
||||
@@ -30,7 +30,9 @@ export type CommonThirdPartyRef
|
||||
export const commonThirdPartyFragment = graphql`
|
||||
fragment CommonThirdPartyCombobox_commonThirdParty on CommonThirdParty @inline {
|
||||
name
|
||||
logoUrl
|
||||
logo {
|
||||
downloadUrl
|
||||
}
|
||||
category
|
||||
websiteUrl
|
||||
headquarterAddress
|
||||
@@ -51,7 +53,9 @@ export const commonThirdPartiesQuery = graphql`
|
||||
commonThirdParties(name: $name) {
|
||||
id
|
||||
name
|
||||
logoUrl
|
||||
logo {
|
||||
downloadUrl
|
||||
}
|
||||
...CommonThirdPartyCombobox_commonThirdParty
|
||||
}
|
||||
}
|
||||
@@ -107,7 +111,7 @@ export function CommonThirdPartyCombobox({
|
||||
>
|
||||
<Avatar
|
||||
name={thirdParty.name}
|
||||
src={thirdParty.logoUrl}
|
||||
src={thirdParty.logo?.downloadUrl}
|
||||
/>
|
||||
{thirdParty.name}
|
||||
</ComboboxItem>
|
||||
|
||||
@@ -36,7 +36,9 @@ const updateBusinessAssociateAgreementMutation = graphql`
|
||||
updateThirdPartyBusinessAssociateAgreement(input: $input) {
|
||||
thirdPartyBusinessAssociateAgreement {
|
||||
id
|
||||
fileUrl
|
||||
file {
|
||||
downloadUrl
|
||||
}
|
||||
validFrom
|
||||
validUntil
|
||||
createdAt
|
||||
|
||||
@@ -36,7 +36,9 @@ const updateDataPrivacyAgreementMutation = graphql`
|
||||
updateThirdPartyDataPrivacyAgreement(input: $input) {
|
||||
thirdPartyDataPrivacyAgreement {
|
||||
id
|
||||
fileUrl
|
||||
file {
|
||||
downloadUrl
|
||||
}
|
||||
validFrom
|
||||
validUntil
|
||||
createdAt
|
||||
|
||||
@@ -38,8 +38,10 @@ const uploadBusinessAssociateAgreementMutation = graphql`
|
||||
uploadThirdPartyBusinessAssociateAgreement(input: $input) {
|
||||
thirdPartyBusinessAssociateAgreement {
|
||||
id
|
||||
fileName
|
||||
fileUrl
|
||||
file {
|
||||
fileName
|
||||
downloadUrl
|
||||
}
|
||||
validFrom
|
||||
validUntil
|
||||
createdAt
|
||||
|
||||
@@ -38,8 +38,10 @@ const uploadDataPrivacyAgreementMutation = graphql`
|
||||
uploadThirdPartyDataPrivacyAgreement(input: $input) {
|
||||
thirdPartyDataPrivacyAgreement {
|
||||
id
|
||||
fileName
|
||||
fileUrl
|
||||
file {
|
||||
fileName
|
||||
downloadUrl
|
||||
}
|
||||
validFrom
|
||||
validUntil
|
||||
createdAt
|
||||
|
||||
@@ -50,8 +50,10 @@ const thirdPartyBusinessAssociateAgreementFragment = graphql`
|
||||
fragment ThirdPartyOverviewTabBusinessAssociateAgreementFragment on ThirdParty {
|
||||
businessAssociateAgreement {
|
||||
id
|
||||
fileName
|
||||
fileUrl
|
||||
file {
|
||||
fileName
|
||||
downloadUrl
|
||||
}
|
||||
validFrom
|
||||
validUntil
|
||||
canUpdate: permission(
|
||||
@@ -68,8 +70,10 @@ const thirdPartyDataPrivacyAgreementFragment = graphql`
|
||||
fragment ThirdPartyOverviewTabDataPrivacyAgreementFragment on ThirdParty {
|
||||
dataPrivacyAgreement {
|
||||
id
|
||||
fileName
|
||||
fileUrl
|
||||
file {
|
||||
fileName
|
||||
downloadUrl
|
||||
}
|
||||
validFrom
|
||||
validUntil
|
||||
canUpdate: permission(action: "core:thirdParty-data-privacy-agreement:update")
|
||||
@@ -299,7 +303,7 @@ export default function ThirdPartyOverviewTab() {
|
||||
</h3>
|
||||
<p className="text-sm text-txt-secondary mt-1">
|
||||
{businessAssociateAgreement
|
||||
? businessAssociateAgreement.fileName
|
||||
? businessAssociateAgreement.file.fileName
|
||||
: __("No business associate agreement available")}
|
||||
</p>
|
||||
{(businessAssociateAgreement?.validFrom
|
||||
@@ -322,8 +326,8 @@ export default function ThirdPartyOverviewTab() {
|
||||
variant="secondary"
|
||||
onClick={() =>
|
||||
downloadFile(
|
||||
businessAssociateAgreement.fileUrl,
|
||||
businessAssociateAgreement.fileName,
|
||||
businessAssociateAgreement.file.downloadUrl,
|
||||
businessAssociateAgreement.file.fileName,
|
||||
)}
|
||||
>
|
||||
{__("Download PDF")}
|
||||
@@ -343,7 +347,7 @@ export default function ThirdPartyOverviewTab() {
|
||||
{businessAssociateAgreement.canDelete && (
|
||||
<DeleteBusinessAssociateAgreementDialog
|
||||
thirdPartyId={thirdParty.id}
|
||||
fileName={businessAssociateAgreement.fileName}
|
||||
fileName={businessAssociateAgreement.file.fileName}
|
||||
onSuccess={() => window.location.reload()}
|
||||
>
|
||||
<Button variant="quaternary" icon={IconTrashCan} />
|
||||
@@ -373,7 +377,7 @@ export default function ThirdPartyOverviewTab() {
|
||||
</h3>
|
||||
<p className="text-sm text-txt-secondary mt-1">
|
||||
{dataPrivacyAgreement
|
||||
? dataPrivacyAgreement.fileName
|
||||
? dataPrivacyAgreement.file.fileName
|
||||
: __("No data privacy agreement available")}
|
||||
</p>
|
||||
{(dataPrivacyAgreement?.validFrom
|
||||
@@ -396,8 +400,8 @@ export default function ThirdPartyOverviewTab() {
|
||||
variant="secondary"
|
||||
onClick={() =>
|
||||
downloadFile(
|
||||
dataPrivacyAgreement.fileUrl,
|
||||
dataPrivacyAgreement.fileName,
|
||||
dataPrivacyAgreement.file.downloadUrl,
|
||||
dataPrivacyAgreement.file.fileName,
|
||||
)}
|
||||
>
|
||||
{__("Download PDF")}
|
||||
@@ -417,7 +421,7 @@ export default function ThirdPartyOverviewTab() {
|
||||
{dataPrivacyAgreement.canDelete && (
|
||||
<DeleteDataPrivacyAgreementDialog
|
||||
thirdPartyId={thirdParty.id}
|
||||
fileName={dataPrivacyAgreement.fileName}
|
||||
fileName={dataPrivacyAgreement.file.fileName}
|
||||
onSuccess={() => window.location.reload()}
|
||||
>
|
||||
<Button variant="quaternary" icon={IconTrashCan} />
|
||||
|
||||
Reference in New Issue
Block a user