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} />
|
||||
|
||||
@@ -29,7 +29,7 @@ import (
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
)
|
||||
|
||||
func TestCommonThirdParties_QueryWithLogoURL(t *testing.T) {
|
||||
func TestCommonThirdParties_QueryWithLogo(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
owner := testutil.NewClient(t, testutil.RoleOwner)
|
||||
@@ -42,25 +42,29 @@ func TestCommonThirdParties_QueryWithLogoURL(t *testing.T) {
|
||||
commonThirdParties(name: $name) {
|
||||
id
|
||||
name
|
||||
logoUrl
|
||||
logo {
|
||||
downloadUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var result struct {
|
||||
CommonThirdParties []struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
LogoURL *string `json:"logoUrl"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Logo *struct {
|
||||
DownloadURL string `json:"downloadUrl"`
|
||||
} `json:"logo"`
|
||||
} `json:"commonThirdParties"`
|
||||
}
|
||||
|
||||
err := owner.Execute(query, map[string]any{"name": name}, &result)
|
||||
require.NoError(t, err, "querying commonThirdParties.logoUrl must not surface a resource-not-found error")
|
||||
require.NoError(t, err, "querying commonThirdParties.logo must not surface a resource-not-found error")
|
||||
require.Len(t, result.CommonThirdParties, 1)
|
||||
assert.Equal(t, id.String(), result.CommonThirdParties[0].ID)
|
||||
assert.Equal(t, name, result.CommonThirdParties[0].Name)
|
||||
assert.Nil(t, result.CommonThirdParties[0].LogoURL)
|
||||
assert.Nil(t, result.CommonThirdParties[0].Logo)
|
||||
}
|
||||
|
||||
func seedCommonThirdParty(t *testing.T, name string) gid.GID {
|
||||
|
||||
113
e2e/console/trust_center_nda_test.go
Normal file
113
e2e/console/trust_center_nda_test.go
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.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 console_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.probo.inc/probo/e2e/internal/testutil"
|
||||
)
|
||||
|
||||
func TestTrustCenter_UploadNDA(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
owner := testutil.NewClient(t, testutil.RoleOwner)
|
||||
organizationID := owner.GetOrganizationID().String()
|
||||
|
||||
const trustCenterQuery = `
|
||||
query($organizationId: ID!) {
|
||||
node(id: $organizationId) {
|
||||
... on Organization {
|
||||
trustCenter {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var trustCenterLookup struct {
|
||||
Node struct {
|
||||
TrustCenter struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"trustCenter"`
|
||||
} `json:"node"`
|
||||
}
|
||||
|
||||
err := owner.Execute(trustCenterQuery, map[string]any{
|
||||
"organizationId": organizationID,
|
||||
}, &trustCenterLookup)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, trustCenterLookup.Node.TrustCenter.ID)
|
||||
|
||||
trustCenterID := trustCenterLookup.Node.TrustCenter.ID
|
||||
|
||||
const uploadMutation = `
|
||||
mutation UploadTrustCenterNDA($input: UploadTrustCenterNDAInput!) {
|
||||
uploadTrustCenterNDA(input: $input) {
|
||||
trustCenter {
|
||||
id
|
||||
nda {
|
||||
id
|
||||
fileName
|
||||
downloadUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
pdfContent := []byte("%PDF-1.4\n1 0 obj\n<< /Type /Catalog >>\nendobj\ntrailer\n<< /Root 1 0 R >>\n%%EOF")
|
||||
|
||||
var uploadResult struct {
|
||||
UploadTrustCenterNDA struct {
|
||||
TrustCenter struct {
|
||||
ID string `json:"id"`
|
||||
Nda *struct {
|
||||
ID string `json:"id"`
|
||||
FileName string `json:"fileName"`
|
||||
DownloadURL string `json:"downloadUrl"`
|
||||
} `json:"nda"`
|
||||
} `json:"trustCenter"`
|
||||
} `json:"uploadTrustCenterNDA"`
|
||||
}
|
||||
|
||||
err = owner.ExecuteWithFile(uploadMutation, map[string]any{
|
||||
"input": map[string]any{
|
||||
"trustCenterId": trustCenterID,
|
||||
"fileName": "nda.pdf",
|
||||
"file": nil,
|
||||
},
|
||||
}, "input.file", testutil.UploadFile{
|
||||
Filename: "nda.pdf",
|
||||
ContentType: "application/pdf",
|
||||
Content: pdfContent,
|
||||
}, &uploadResult)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, trustCenterID, uploadResult.UploadTrustCenterNDA.TrustCenter.ID)
|
||||
require.NotNil(t, uploadResult.UploadTrustCenterNDA.TrustCenter.Nda)
|
||||
assert.Equal(t, "nda.pdf", uploadResult.UploadTrustCenterNDA.TrustCenter.Nda.FileName)
|
||||
assert.NotEmpty(t, uploadResult.UploadTrustCenterNDA.TrustCenter.Nda.DownloadURL)
|
||||
assert.True(
|
||||
t,
|
||||
strings.Contains(uploadResult.UploadTrustCenterNDA.TrustCenter.Nda.DownloadURL, "/api/files/v1/"),
|
||||
"downloadUrl must route through the files API, got %q",
|
||||
uploadResult.UploadTrustCenterNDA.TrustCenter.Nda.DownloadURL,
|
||||
)
|
||||
}
|
||||
@@ -166,6 +166,25 @@ func (s *Service) GeneratePresignedFileURL(
|
||||
return presignedReq.URL, nil
|
||||
}
|
||||
|
||||
// DownloadAPIPath returns the stable files API path for a stored file.
|
||||
func DownloadAPIPath(file *coredata.File) string {
|
||||
if file.Visibility == coredata.FileVisibilityPublic {
|
||||
return "/api/files/v1/public/" + file.ID.String()
|
||||
}
|
||||
|
||||
return "/api/files/v1/" + file.ID.String()
|
||||
}
|
||||
|
||||
// BuildDownloadURL returns the absolute app URL that routes through the files API.
|
||||
func (s *Service) BuildDownloadURL(file *coredata.File) (string, error) {
|
||||
url, err := s.baseURL.AppendPath(DownloadAPIPath(file)).String()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot build file URL: %w", err)
|
||||
}
|
||||
|
||||
return url, nil
|
||||
}
|
||||
|
||||
// GenerateFileURL loads a public file from DB and returns the stable app URL
|
||||
// /api/files/v1/public/{id}. Used when a long-lived embeddable URL is needed
|
||||
// (e.g. trust center logos).
|
||||
@@ -188,12 +207,7 @@ func (s *Service) GenerateFileURL(
|
||||
return "", err
|
||||
}
|
||||
|
||||
url, err := s.baseURL.AppendPath("/api/files/v1/public/" + fileID.String()).String()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("cannot build file URL: %w", err)
|
||||
}
|
||||
|
||||
return url, nil
|
||||
return s.BuildDownloadURL(file)
|
||||
}
|
||||
|
||||
// GeneratePublicPresignedFileURL loads a public file from DB and returns a
|
||||
|
||||
@@ -19,8 +19,33 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.probo.inc/probo/pkg/baseurl"
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/filemanager"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
)
|
||||
|
||||
func TestDownloadAPIPath_IncludesPublicSegmentForPublicFiles(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
file := &coredata.File{
|
||||
ID: gid.New(gid.NilTenant, coredata.FileEntityType),
|
||||
Visibility: coredata.FileVisibilityPublic,
|
||||
}
|
||||
|
||||
assert.Equal(t, "/api/files/v1/public/"+file.ID.String(), filemanager.DownloadAPIPath(file))
|
||||
}
|
||||
|
||||
func TestDownloadAPIPath_UsesPrivateSegmentForPrivateFiles(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
file := &coredata.File{
|
||||
ID: gid.New(gid.NilTenant, coredata.FileEntityType),
|
||||
Visibility: coredata.FileVisibilityPrivate,
|
||||
}
|
||||
|
||||
assert.Equal(t, "/api/files/v1/"+file.ID.String(), filemanager.DownloadAPIPath(file))
|
||||
}
|
||||
|
||||
func TestGenerateFileURL_PathIncludesPublicSegment(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -29,10 +54,15 @@ func TestGenerateFileURL_PathIncludesPublicSegment(t *testing.T) {
|
||||
t.Fatalf("cannot parse base URL: %v", err)
|
||||
}
|
||||
|
||||
url, err := base.AppendPath("/api/files/v1/public/some-id").String()
|
||||
file := &coredata.File{
|
||||
ID: gid.New(gid.NilTenant, coredata.FileEntityType),
|
||||
Visibility: coredata.FileVisibilityPublic,
|
||||
}
|
||||
|
||||
url, err := base.AppendPath(filemanager.DownloadAPIPath(file)).String()
|
||||
if err != nil {
|
||||
t.Fatalf("cannot build URL: %v", err)
|
||||
}
|
||||
|
||||
assert.Equal(t, "https://app.example.com/api/files/v1/public/some-id", url)
|
||||
assert.Equal(t, "https://app.example.com/api/files/v1/public/"+file.ID.String(), url)
|
||||
}
|
||||
|
||||
@@ -1410,12 +1410,12 @@ func (s OrganizationService) GenerateLogoURL(
|
||||
return nil, fmt.Errorf("cannot generate logo URL: %w", err)
|
||||
}
|
||||
|
||||
presignedURL, err := s.fm.GeneratePresignedFileURL(ctx, file, expiresIn)
|
||||
downloadURL, err := s.fm.BuildDownloadURL(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot generate file URL: %w", err)
|
||||
}
|
||||
|
||||
return &presignedURL, nil
|
||||
return &downloadURL, nil
|
||||
}
|
||||
|
||||
func (s OrganizationService) GenerateHorizontalLogoURL(
|
||||
@@ -1456,12 +1456,12 @@ func (s OrganizationService) GenerateHorizontalLogoURL(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
presignedURL, err := s.fm.GeneratePresignedFileURL(ctx, file, expiresIn)
|
||||
downloadURL, err := s.fm.BuildDownloadURL(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot generate file URL: %w", err)
|
||||
}
|
||||
|
||||
return &presignedURL, nil
|
||||
return &downloadURL, nil
|
||||
}
|
||||
|
||||
func (s OrganizationService) DeleteSAMLConfiguration(
|
||||
|
||||
@@ -216,17 +216,12 @@ func (s TrustCenterService) UploadNDA(
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
objectKey, err := uuid.NewV7()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("cannot generate object key: %w", err)
|
||||
}
|
||||
|
||||
var (
|
||||
trustCenter *coredata.TrustCenter
|
||||
file *coredata.File
|
||||
)
|
||||
|
||||
err = s.svc.pg.WithTx(
|
||||
err := s.svc.pg.WithTx(
|
||||
ctx,
|
||||
func(ctx context.Context, conn pg.Tx) error {
|
||||
trustCenter = &coredata.TrustCenter{}
|
||||
@@ -234,30 +229,18 @@ func (s TrustCenterService) UploadNDA(
|
||||
return fmt.Errorf("cannot load trust center: %w", err)
|
||||
}
|
||||
|
||||
mimeType := mime.TypeByExtension(filepath.Ext(req.FileName))
|
||||
|
||||
_, err := s.svc.s3.PutObject(ctx, &s3.PutObjectInput{
|
||||
Bucket: &s.svc.bucket,
|
||||
Key: new(objectKey.String()),
|
||||
Body: req.File,
|
||||
ContentType: &mimeType,
|
||||
CacheControl: new("private, max-age=3600"),
|
||||
Metadata: map[string]string{
|
||||
"type": "trust-center-nda",
|
||||
"trust-center-id": req.TrustCenterID.String(),
|
||||
"organization-id": trustCenter.OrganizationID.String(),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot upload file to S3: %w", err)
|
||||
if trustCenter.OrganizationID == gid.Nil {
|
||||
return fmt.Errorf("trust center %s has no organization", req.TrustCenterID)
|
||||
}
|
||||
|
||||
headOutput, err := s.svc.s3.HeadObject(ctx, &s3.HeadObjectInput{
|
||||
Bucket: new(s.svc.bucket),
|
||||
Key: new(objectKey.String()),
|
||||
})
|
||||
objectKey, err := uuid.NewV7()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot get object metadata: %w", err)
|
||||
return fmt.Errorf("cannot generate object key: %w", err)
|
||||
}
|
||||
|
||||
mimeType := mime.TypeByExtension(filepath.Ext(req.FileName))
|
||||
if mimeType == "" {
|
||||
mimeType = "application/octet-stream"
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
@@ -270,12 +253,27 @@ func (s TrustCenterService) UploadNDA(
|
||||
MimeType: mimeType,
|
||||
FileName: req.FileName,
|
||||
FileKey: objectKey.String(),
|
||||
FileSize: *headOutput.ContentLength,
|
||||
Visibility: coredata.FileVisibilityPrivate,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
fileSize, err := s.svc.fileManager.PutFile(
|
||||
ctx,
|
||||
file,
|
||||
req.File,
|
||||
map[string]string{
|
||||
"type": "trust-center-nda",
|
||||
"trust-center-id": req.TrustCenterID.String(),
|
||||
"organization-id": trustCenter.OrganizationID.String(),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot upload file to S3: %w", err)
|
||||
}
|
||||
|
||||
file.FileSize = fileSize
|
||||
|
||||
if err := file.Insert(ctx, conn, scope); err != nil {
|
||||
return fmt.Errorf("cannot insert file: %w", err)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ package console_v1
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
@@ -332,15 +331,7 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var file *coredata.File
|
||||
if trustCenter.NonDisclosureAgreementFileID != nil {
|
||||
file, err = r.probo.Files.Get(ctx, scope, *trustCenter.NonDisclosureAgreementFileID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot get NDA file: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return types.NewTrustCenter(trustCenter, file), nil
|
||||
return types.NewTrustCenter(trustCenter), nil
|
||||
}
|
||||
case coredata.TrustCenterAccessEntityType:
|
||||
action = probo.ActionTrustCenterAccessGet
|
||||
|
||||
@@ -8,30 +8,22 @@ package console_v1
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.gearno.de/kit/log"
|
||||
"go.probo.inc/probo/pkg/probo"
|
||||
"go.probo.inc/probo/pkg/server/api/console/v1/schema"
|
||||
"go.probo.inc/probo/pkg/server/api/console/v1/types"
|
||||
"go.probo.inc/probo/pkg/server/gqlutils"
|
||||
)
|
||||
|
||||
// LogoURL is the resolver for the logoUrl field.
|
||||
func (r *commonThirdPartyResolver) LogoURL(ctx context.Context, obj *types.CommonThirdParty) (*string, error) {
|
||||
// Logo is the resolver for the logo field.
|
||||
func (r *commonThirdPartyResolver) Logo(ctx context.Context, obj *types.CommonThirdParty) (*types.File, error) {
|
||||
if _, err := r.authorize(ctx, obj.ID, probo.ActionCommonThirdPartyGet); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if obj.LogoFileID == nil {
|
||||
if obj.Logo == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
logoURL, err := r.thirdParty.GenerateLogoURL(ctx, *obj.LogoFileID)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot generate common third party logo URL", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return logoURL, nil
|
||||
return r.loadFile(ctx, obj.Logo.ID)
|
||||
}
|
||||
|
||||
// CommonThirdParty returns schema.CommonThirdPartyResolver implementation.
|
||||
|
||||
@@ -8,34 +8,23 @@ package console_v1
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.probo.inc/probo/pkg/probo"
|
||||
"go.probo.inc/probo/pkg/server/api/console/v1/schema"
|
||||
"go.probo.inc/probo/pkg/server/api/console/v1/types"
|
||||
)
|
||||
|
||||
// CertificateFileURL is the resolver for the certificateFileUrl field.
|
||||
func (r *electronicSignatureResolver) CertificateFileURL(ctx context.Context, obj *types.ElectronicSignature) (*string, error) {
|
||||
// Certificate is the resolver for the certificate field.
|
||||
func (r *electronicSignatureResolver) Certificate(ctx context.Context, obj *types.ElectronicSignature) (*types.File, error) {
|
||||
if _, err := r.authorize(ctx, obj.ID, probo.ActionElectronicSignatureGet); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
signature, err := r.esign.GetSignatureByID(ctx, obj.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot load signature: %w", err)
|
||||
}
|
||||
|
||||
if signature.CertificateFileID == nil {
|
||||
if obj.Certificate == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
url, err := r.esign.GenerateCertificateFileURL(ctx, *signature.CertificateFileID, 1*time.Hour)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot generate certificate file URL: %w", err)
|
||||
}
|
||||
|
||||
return &url, nil
|
||||
return r.loadFile(ctx, obj.Certificate.ID)
|
||||
}
|
||||
|
||||
// Events is the resolver for the events field.
|
||||
|
||||
44
pkg/server/api/console/v1/file_loader.go
Normal file
44
pkg/server/api/console/v1/file_loader.go
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.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 console_v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/vikstrous/dataloadgen"
|
||||
"go.gearno.de/kit/log"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/server/api/console/v1/dataloader"
|
||||
"go.probo.inc/probo/pkg/server/api/console/v1/types"
|
||||
"go.probo.inc/probo/pkg/server/gqlutils"
|
||||
)
|
||||
|
||||
func (r *Resolver) loadFile(ctx context.Context, fileID gid.GID) (*types.File, error) {
|
||||
loaders := dataloader.FromContext(ctx)
|
||||
|
||||
file, err := loaders.File.Load(ctx, fileID)
|
||||
if err != nil {
|
||||
if errors.Is(err, dataloadgen.ErrNotFound) {
|
||||
return nil, gqlutils.NotFound(ctx, err)
|
||||
}
|
||||
|
||||
r.logger.ErrorCtx(ctx, "cannot load file", log.Error(err))
|
||||
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return types.NewFile(file, r.baseURL), nil
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package console_v1
|
||||
|
||||
// This file will be automatically regenerated based on the schema, any resolver
|
||||
// implementations
|
||||
// will be copied through when generating and any unknown code will be moved to the end.
|
||||
// Code generated by github.com/99designs/gqlgen version v0.17.90
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"go.gearno.de/kit/log"
|
||||
"go.probo.inc/probo/pkg/probo"
|
||||
"go.probo.inc/probo/pkg/server/api/console/v1/schema"
|
||||
"go.probo.inc/probo/pkg/server/api/console/v1/types"
|
||||
"go.probo.inc/probo/pkg/server/gqlutils"
|
||||
)
|
||||
|
||||
// DownloadURL is the resolver for the downloadUrl field.
|
||||
func (r *fileResolver) DownloadURL(ctx context.Context, obj *types.File) (string, error) {
|
||||
scope, err := r.authorize(ctx, obj.ID, probo.ActionFileGet)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
downloadUrl, err := r.probo.Files.GenerateFileURL(ctx, scope, obj.ID, 60*time.Second)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot generate download URL", log.Error(err))
|
||||
return "", gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return downloadUrl, nil
|
||||
}
|
||||
|
||||
// File returns schema.FileResolver implementation.
|
||||
func (r *Resolver) File() schema.FileResolver { return &fileResolver{r} }
|
||||
|
||||
type fileResolver struct{ *Resolver }
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/vikstrous/dataloadgen"
|
||||
"go.gearno.de/kit/log"
|
||||
@@ -81,24 +80,30 @@ func (r *frameworkResolver) Controls(ctx context.Context, obj *types.Framework,
|
||||
return types.NewControlConnection(page, r, obj.ID, controlFilter), nil
|
||||
}
|
||||
|
||||
// LightLogoURL is the resolver for the lightLogoURL field.
|
||||
func (r *frameworkResolver) LightLogoURL(ctx context.Context, obj *types.Framework) (*string, error) {
|
||||
scope, err := r.authorize(ctx, obj.ID, probo.ActionFrameworkGet)
|
||||
if err != nil {
|
||||
// LightLogo is the resolver for the lightLogo field.
|
||||
func (r *frameworkResolver) LightLogo(ctx context.Context, obj *types.Framework) (*types.File, error) {
|
||||
if _, err := r.authorize(ctx, obj.ID, probo.ActionFrameworkGet); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.probo.Frameworks.GenerateLightLogoURL(ctx, scope, obj.ID, 1*time.Hour)
|
||||
if obj.LightLogo == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return r.loadFile(ctx, obj.LightLogo.ID)
|
||||
}
|
||||
|
||||
// DarkLogoURL is the resolver for the darkLogoURL field.
|
||||
func (r *frameworkResolver) DarkLogoURL(ctx context.Context, obj *types.Framework) (*string, error) {
|
||||
scope, err := r.authorize(ctx, obj.ID, probo.ActionFrameworkGet)
|
||||
if err != nil {
|
||||
// DarkLogo is the resolver for the darkLogo field.
|
||||
func (r *frameworkResolver) DarkLogo(ctx context.Context, obj *types.Framework) (*types.File, error) {
|
||||
if _, err := r.authorize(ctx, obj.ID, probo.ActionFrameworkGet); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.probo.Frameworks.GenerateDarkLogoURL(ctx, scope, obj.ID, 1*time.Hour)
|
||||
if obj.DarkLogo == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return r.loadFile(ctx, obj.DarkLogo.ID)
|
||||
}
|
||||
|
||||
// Permission is the resolver for the permission field.
|
||||
|
||||
@@ -30,5 +30,5 @@ type CommonThirdParty
|
||||
trustPageUrl: String
|
||||
statusPageUrl: String
|
||||
termsOfServiceUrl: String
|
||||
logoUrl: String @goField(forceResolver: true)
|
||||
logo: File @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ type ElectronicSignature implements Node {
|
||||
consentText: String!
|
||||
lastError: String
|
||||
signedAt: Datetime
|
||||
certificateFileUrl: String @goField(forceResolver: true)
|
||||
certificate: File @goField(forceResolver: true)
|
||||
events: [ElectronicSignatureEvent!]! @goField(forceResolver: true)
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
|
||||
@@ -3,7 +3,7 @@ type File {
|
||||
mimeType: String!
|
||||
fileName: String!
|
||||
size: BigInt!
|
||||
downloadUrl: String! @goField(forceResolver: true)
|
||||
downloadUrl: String!
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ type Framework implements Node {
|
||||
orderBy: ControlOrder
|
||||
filter: ControlFilter
|
||||
): ControlConnection! @goField(forceResolver: true)
|
||||
lightLogoURL: String @goField(forceResolver: true)
|
||||
darkLogoURL: String @goField(forceResolver: true)
|
||||
lightLogo: File @goField(forceResolver: true)
|
||||
darkLogo: File @goField(forceResolver: true)
|
||||
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
|
||||
@@ -90,8 +90,8 @@ type UpdateOrganizationContextPayload {
|
||||
type Organization implements Node {
|
||||
id: ID!
|
||||
name: String!
|
||||
logoUrl: String @goField(forceResolver: true)
|
||||
horizontalLogoUrl: String @goField(forceResolver: true)
|
||||
logo: File @goField(forceResolver: true)
|
||||
horizontalLogo: File @goField(forceResolver: true)
|
||||
|
||||
description: String
|
||||
websiteUrl: String
|
||||
|
||||
@@ -342,9 +342,7 @@ type ThirdPartyBusinessAssociateAgreement implements Node {
|
||||
thirdParty: ThirdParty! @goField(forceResolver: true)
|
||||
validFrom: Datetime
|
||||
validUntil: Datetime
|
||||
fileName: String!
|
||||
fileUrl: String! @goField(forceResolver: true)
|
||||
fileSize: BigInt!
|
||||
file: File! @goField(forceResolver: true)
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
|
||||
@@ -380,9 +378,7 @@ type ThirdPartyDataPrivacyAgreement implements Node {
|
||||
thirdParty: ThirdParty! @goField(forceResolver: true)
|
||||
validFrom: Datetime
|
||||
validUntil: Datetime
|
||||
fileName: String!
|
||||
fileUrl: String! @goField(forceResolver: true)
|
||||
fileSize: BigInt!
|
||||
file: File! @goField(forceResolver: true)
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
|
||||
|
||||
@@ -249,10 +249,9 @@ type TrustCenter implements Node
|
||||
id: ID!
|
||||
active: Boolean!
|
||||
searchEngineIndexing: SearchEngineIndexing!
|
||||
logoFileUrl: String @goField(forceResolver: true)
|
||||
darkLogoFileUrl: String @goField(forceResolver: true)
|
||||
ndaFileName: String
|
||||
ndaFileUrl: String @goField(forceResolver: true)
|
||||
logo: File @goField(forceResolver: true)
|
||||
darkLogo: File @goField(forceResolver: true)
|
||||
nda: File @goField(forceResolver: true)
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
organization: Organization! @goField(forceResolver: true)
|
||||
@@ -368,7 +367,7 @@ type TrustCenterReference implements Node {
|
||||
name: String!
|
||||
description: String
|
||||
websiteUrl: String!
|
||||
logoUrl: String! @goField(forceResolver: true)
|
||||
logo: File! @goField(forceResolver: true)
|
||||
rank: Int!
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
@@ -443,7 +442,7 @@ type TrustCenterFile implements Node {
|
||||
id: ID!
|
||||
name: String!
|
||||
category: String!
|
||||
fileUrl: String! @goField(forceResolver: true)
|
||||
file: File! @goField(forceResolver: true)
|
||||
trustCenterVisibility: TrustCenterVisibility!
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.gearno.de/kit/log"
|
||||
"go.probo.inc/probo/pkg/agentrun"
|
||||
@@ -58,36 +57,30 @@ func (r *mutationResolver) UpdateOrganizationContext(ctx context.Context, input
|
||||
}, nil
|
||||
}
|
||||
|
||||
// LogoURL is the resolver for the logoUrl field.
|
||||
func (r *organizationResolver) LogoURL(ctx context.Context, obj *types.Organization) (*string, error) {
|
||||
scope, err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGetLogoUrl)
|
||||
if err != nil {
|
||||
// Logo is the resolver for the logo field.
|
||||
func (r *organizationResolver) Logo(ctx context.Context, obj *types.Organization) (*types.File, error) {
|
||||
if _, err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGetLogoUrl); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logoURL, err := r.probo.Organizations.GenerateLogoURL(ctx, scope, obj.ID, 1*time.Hour)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot generate logo url", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
if obj.Logo == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return logoURL, nil
|
||||
return r.loadFile(ctx, obj.Logo.ID)
|
||||
}
|
||||
|
||||
// HorizontalLogoURL is the resolver for the horizontalLogoUrl field.
|
||||
func (r *organizationResolver) HorizontalLogoURL(ctx context.Context, obj *types.Organization) (*string, error) {
|
||||
scope, err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGetHorizontalLogoUrl)
|
||||
if err != nil {
|
||||
// HorizontalLogo is the resolver for the horizontalLogo field.
|
||||
func (r *organizationResolver) HorizontalLogo(ctx context.Context, obj *types.Organization) (*types.File, error) {
|
||||
if _, err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGetHorizontalLogoUrl); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
horizontalLogoURL, err := r.probo.Organizations.GenerateHorizontalLogoURL(ctx, scope, obj.ID, 1*time.Hour)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot generate horizontal logo url", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
if obj.HorizontalLogo == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return horizontalLogoURL, nil
|
||||
return r.loadFile(ctx, obj.HorizontalLogo.ID)
|
||||
}
|
||||
|
||||
// Context is the resolver for the context field.
|
||||
@@ -1175,16 +1168,7 @@ func (r *organizationResolver) TrustCenter(ctx context.Context, obj *types.Organ
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
var file *coredata.File
|
||||
if trustCenter.NonDisclosureAgreementFileID != nil {
|
||||
file, err = r.probo.Files.Get(ctx, scope, *trustCenter.NonDisclosureAgreementFileID)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot get NDA file", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
return types.NewTrustCenter(trustCenter, file), nil
|
||||
return types.NewTrustCenter(trustCenter), nil
|
||||
}
|
||||
|
||||
// CustomDomain is the resolver for the customDomain field.
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
pgx "github.com/jackc/pgx/v5"
|
||||
"github.com/vikstrous/dataloadgen"
|
||||
@@ -359,7 +358,7 @@ func (r *mutationResolver) UploadThirdPartyBusinessAssociateAgreement(ctx contex
|
||||
return nil, err
|
||||
}
|
||||
|
||||
thirdPartyBusinessAssociateAgreement, file, err := r.probo.ThirdPartyBusinessAssociateAgreements.Upload(
|
||||
thirdPartyBusinessAssociateAgreement, _, err := r.probo.ThirdPartyBusinessAssociateAgreements.Upload(
|
||||
ctx, scope,
|
||||
input.ThirdPartyID,
|
||||
&probo.ThirdPartyBusinessAssociateAgreementCreateRequest{
|
||||
@@ -380,7 +379,7 @@ func (r *mutationResolver) UploadThirdPartyBusinessAssociateAgreement(ctx contex
|
||||
}
|
||||
|
||||
return &types.UploadThirdPartyBusinessAssociateAgreementPayload{
|
||||
ThirdPartyBusinessAssociateAgreement: types.NewThirdPartyBusinessAssociateAgreement(thirdPartyBusinessAssociateAgreement, file),
|
||||
ThirdPartyBusinessAssociateAgreement: types.NewThirdPartyBusinessAssociateAgreement(thirdPartyBusinessAssociateAgreement),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -391,7 +390,7 @@ func (r *mutationResolver) UpdateThirdPartyBusinessAssociateAgreement(ctx contex
|
||||
return nil, err
|
||||
}
|
||||
|
||||
thirdPartyBusinessAssociateAgreement, file, err := r.probo.ThirdPartyBusinessAssociateAgreements.Update(
|
||||
thirdPartyBusinessAssociateAgreement, _, err := r.probo.ThirdPartyBusinessAssociateAgreements.Update(
|
||||
ctx, scope,
|
||||
input.ThirdPartyID,
|
||||
&probo.ThirdPartyBusinessAssociateAgreementUpdateRequest{
|
||||
@@ -410,7 +409,7 @@ func (r *mutationResolver) UpdateThirdPartyBusinessAssociateAgreement(ctx contex
|
||||
}
|
||||
|
||||
return &types.UpdateThirdPartyBusinessAssociateAgreementPayload{
|
||||
ThirdPartyBusinessAssociateAgreement: types.NewThirdPartyBusinessAssociateAgreement(thirdPartyBusinessAssociateAgreement, file),
|
||||
ThirdPartyBusinessAssociateAgreement: types.NewThirdPartyBusinessAssociateAgreement(thirdPartyBusinessAssociateAgreement),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -438,7 +437,7 @@ func (r *mutationResolver) UploadThirdPartyDataPrivacyAgreement(ctx context.Cont
|
||||
return nil, err
|
||||
}
|
||||
|
||||
thirdPartyDataPrivacyAgreement, file, err := r.probo.ThirdPartyDataPrivacyAgreements.Upload(
|
||||
thirdPartyDataPrivacyAgreement, _, err := r.probo.ThirdPartyDataPrivacyAgreements.Upload(
|
||||
ctx, scope,
|
||||
input.ThirdPartyID,
|
||||
&probo.ThirdPartyDataPrivacyAgreementCreateRequest{
|
||||
@@ -459,7 +458,7 @@ func (r *mutationResolver) UploadThirdPartyDataPrivacyAgreement(ctx context.Cont
|
||||
}
|
||||
|
||||
return &types.UploadThirdPartyDataPrivacyAgreementPayload{
|
||||
ThirdPartyDataPrivacyAgreement: types.NewThirdPartyDataPrivacyAgreement(thirdPartyDataPrivacyAgreement, file),
|
||||
ThirdPartyDataPrivacyAgreement: types.NewThirdPartyDataPrivacyAgreement(thirdPartyDataPrivacyAgreement),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -470,7 +469,7 @@ func (r *mutationResolver) UpdateThirdPartyDataPrivacyAgreement(ctx context.Cont
|
||||
return nil, err
|
||||
}
|
||||
|
||||
thirdPartyDataPrivacyAgreement, file, err := r.probo.ThirdPartyDataPrivacyAgreements.Update(
|
||||
thirdPartyDataPrivacyAgreement, _, err := r.probo.ThirdPartyDataPrivacyAgreements.Update(
|
||||
ctx, scope,
|
||||
input.ThirdPartyID,
|
||||
&probo.ThirdPartyDataPrivacyAgreementUpdateRequest{
|
||||
@@ -489,7 +488,7 @@ func (r *mutationResolver) UpdateThirdPartyDataPrivacyAgreement(ctx context.Cont
|
||||
}
|
||||
|
||||
return &types.UpdateThirdPartyDataPrivacyAgreementPayload{
|
||||
ThirdPartyDataPrivacyAgreement: types.NewThirdPartyDataPrivacyAgreement(thirdPartyDataPrivacyAgreement, file),
|
||||
ThirdPartyDataPrivacyAgreement: types.NewThirdPartyDataPrivacyAgreement(thirdPartyDataPrivacyAgreement),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -667,7 +666,7 @@ func (r *thirdPartyResolver) BusinessAssociateAgreement(ctx context.Context, obj
|
||||
return nil, err
|
||||
}
|
||||
|
||||
thirdPartyBusinessAssociateAgreement, file, err := r.probo.ThirdPartyBusinessAssociateAgreements.GetByThirdPartyID(ctx, scope, obj.ID)
|
||||
thirdPartyBusinessAssociateAgreement, _, err := r.probo.ThirdPartyBusinessAssociateAgreements.GetByThirdPartyID(ctx, scope, obj.ID)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, nil
|
||||
@@ -678,7 +677,7 @@ func (r *thirdPartyResolver) BusinessAssociateAgreement(ctx context.Context, obj
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return types.NewThirdPartyBusinessAssociateAgreement(thirdPartyBusinessAssociateAgreement, file), nil
|
||||
return types.NewThirdPartyBusinessAssociateAgreement(thirdPartyBusinessAssociateAgreement), nil
|
||||
}
|
||||
|
||||
// DataPrivacyAgreement is the resolver for the dataPrivacyAgreement field.
|
||||
@@ -688,7 +687,7 @@ func (r *thirdPartyResolver) DataPrivacyAgreement(ctx context.Context, obj *type
|
||||
return nil, err
|
||||
}
|
||||
|
||||
thirdPartyDataPrivacyAgreement, file, err := r.probo.ThirdPartyDataPrivacyAgreements.GetByThirdPartyID(ctx, scope, obj.ID)
|
||||
thirdPartyDataPrivacyAgreement, _, err := r.probo.ThirdPartyDataPrivacyAgreements.GetByThirdPartyID(ctx, scope, obj.ID)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return nil, nil
|
||||
@@ -699,7 +698,7 @@ func (r *thirdPartyResolver) DataPrivacyAgreement(ctx context.Context, obj *type
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return types.NewThirdPartyDataPrivacyAgreement(thirdPartyDataPrivacyAgreement, file), nil
|
||||
return types.NewThirdPartyDataPrivacyAgreement(thirdPartyDataPrivacyAgreement), nil
|
||||
}
|
||||
|
||||
// Contacts is the resolver for the contacts field.
|
||||
@@ -994,20 +993,13 @@ func (r *thirdPartyBusinessAssociateAgreementResolver) ThirdParty(ctx context.Co
|
||||
return types.NewThirdParty(thirdParty), nil
|
||||
}
|
||||
|
||||
// FileURL is the resolver for the fileUrl field.
|
||||
func (r *thirdPartyBusinessAssociateAgreementResolver) FileURL(ctx context.Context, obj *types.ThirdPartyBusinessAssociateAgreement) (string, error) {
|
||||
scope, err := r.authorize(ctx, obj.ID, probo.ActionFileGet)
|
||||
if err != nil {
|
||||
return "", err
|
||||
// File is the resolver for the file field.
|
||||
func (r *thirdPartyBusinessAssociateAgreementResolver) File(ctx context.Context, obj *types.ThirdPartyBusinessAssociateAgreement) (*types.File, error) {
|
||||
if _, err := r.authorize(ctx, obj.ID, probo.ActionFileGet); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fileURL, err := r.probo.ThirdPartyBusinessAssociateAgreements.GenerateFileURL(ctx, scope, obj.ID, 1*time.Hour)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot generate file URL", log.Error(err))
|
||||
return "", gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return fileURL, nil
|
||||
return r.loadFile(ctx, obj.File.ID)
|
||||
}
|
||||
|
||||
// Permission is the resolver for the permission field.
|
||||
@@ -1185,20 +1177,13 @@ func (r *thirdPartyDataPrivacyAgreementResolver) ThirdParty(ctx context.Context,
|
||||
return types.NewThirdParty(thirdParty), nil
|
||||
}
|
||||
|
||||
// FileURL is the resolver for the fileUrl field.
|
||||
func (r *thirdPartyDataPrivacyAgreementResolver) FileURL(ctx context.Context, obj *types.ThirdPartyDataPrivacyAgreement) (string, error) {
|
||||
scope, err := r.authorize(ctx, obj.ID, probo.ActionFileGet)
|
||||
if err != nil {
|
||||
return "", err
|
||||
// File is the resolver for the file field.
|
||||
func (r *thirdPartyDataPrivacyAgreementResolver) File(ctx context.Context, obj *types.ThirdPartyDataPrivacyAgreement) (*types.File, error) {
|
||||
if _, err := r.authorize(ctx, obj.ID, probo.ActionFileGet); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fileURL, err := r.probo.ThirdPartyDataPrivacyAgreements.GenerateFileURL(ctx, scope, obj.ID, 1*time.Hour)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot generate file URL", log.Error(err))
|
||||
return "", gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return fileURL, nil
|
||||
return r.loadFile(ctx, obj.File.ID)
|
||||
}
|
||||
|
||||
// Permission is the resolver for the permission field.
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/vikstrous/dataloadgen"
|
||||
"go.gearno.de/kit/log"
|
||||
@@ -63,7 +62,7 @@ func (r *mutationResolver) UpdateTrustCenter(ctx context.Context, input types.Up
|
||||
return nil, err
|
||||
}
|
||||
|
||||
trustCenter, file, err := r.probo.TrustCenters.Update(
|
||||
trustCenter, _, err := r.probo.TrustCenters.Update(
|
||||
ctx, scope,
|
||||
&probo.UpdateTrustCenterRequest{
|
||||
ID: input.TrustCenterID,
|
||||
@@ -82,7 +81,7 @@ func (r *mutationResolver) UpdateTrustCenter(ctx context.Context, input types.Up
|
||||
}
|
||||
|
||||
return &types.UpdateTrustCenterPayload{
|
||||
TrustCenter: types.NewTrustCenter(trustCenter, file),
|
||||
TrustCenter: types.NewTrustCenter(trustCenter),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -93,7 +92,7 @@ func (r *mutationResolver) UploadTrustCenterNda(ctx context.Context, input types
|
||||
return nil, err
|
||||
}
|
||||
|
||||
trustCenter, file, err := r.probo.TrustCenters.UploadNDA(
|
||||
trustCenter, _, err := r.probo.TrustCenters.UploadNDA(
|
||||
ctx, scope,
|
||||
&probo.UploadTrustCenterNDARequest{
|
||||
TrustCenterID: input.TrustCenterID,
|
||||
@@ -112,7 +111,7 @@ func (r *mutationResolver) UploadTrustCenterNda(ctx context.Context, input types
|
||||
}
|
||||
|
||||
return &types.UploadTrustCenterNDAPayload{
|
||||
TrustCenter: types.NewTrustCenter(trustCenter, file),
|
||||
TrustCenter: types.NewTrustCenter(trustCenter),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -123,14 +122,14 @@ func (r *mutationResolver) DeleteTrustCenterNda(ctx context.Context, input types
|
||||
return nil, err
|
||||
}
|
||||
|
||||
trustCenter, file, err := r.probo.TrustCenters.DeleteNDA(ctx, scope, input.TrustCenterID)
|
||||
trustCenter, _, err := r.probo.TrustCenters.DeleteNDA(ctx, scope, input.TrustCenterID)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot delete trust center NDA", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return &types.DeleteTrustCenterNDAPayload{
|
||||
TrustCenter: types.NewTrustCenter(trustCenter, file),
|
||||
TrustCenter: types.NewTrustCenter(trustCenter),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -179,7 +178,7 @@ func (r *mutationResolver) UpdateTrustCenterBrand(ctx context.Context, input typ
|
||||
}
|
||||
}
|
||||
|
||||
trustCenter, file, err := r.probo.TrustCenters.UpdateTrustCenterBrand(ctx, scope, req)
|
||||
trustCenter, _, err := r.probo.TrustCenters.UpdateTrustCenterBrand(ctx, scope, req)
|
||||
if err != nil {
|
||||
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
|
||||
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
|
||||
@@ -191,7 +190,7 @@ func (r *mutationResolver) UpdateTrustCenterBrand(ctx context.Context, input typ
|
||||
}
|
||||
|
||||
return &types.UpdateTrustCenterBrandPayload{
|
||||
TrustCenter: types.NewTrustCenter(trustCenter, file),
|
||||
TrustCenter: types.NewTrustCenter(trustCenter),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -689,59 +688,45 @@ func (r *mutationResolver) DeleteCustomDomain(ctx context.Context, input types.D
|
||||
}, nil
|
||||
}
|
||||
|
||||
// LogoFileURL is the resolver for the logoFileUrl field.
|
||||
func (r *trustCenterResolver) LogoFileURL(ctx context.Context, obj *types.TrustCenter) (*string, error) {
|
||||
scope, err := r.authorize(ctx, obj.ID, probo.ActionTrustCenterGet)
|
||||
if err != nil {
|
||||
// Logo is the resolver for the logo field.
|
||||
func (r *trustCenterResolver) Logo(ctx context.Context, obj *types.TrustCenter) (*types.File, error) {
|
||||
if _, err := r.authorize(ctx, obj.ID, probo.ActionTrustCenterGet); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logoURL, err := r.probo.TrustCenters.GenerateLogoURL(ctx, scope, obj.ID, 1*time.Hour)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot generate logo url", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
if obj.Logo == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return logoURL, nil
|
||||
return r.loadFile(ctx, obj.Logo.ID)
|
||||
}
|
||||
|
||||
// DarkLogoFileURL is the resolver for the darkLogoFileUrl field.
|
||||
func (r *trustCenterResolver) DarkLogoFileURL(ctx context.Context, obj *types.TrustCenter) (*string, error) {
|
||||
scope, err := r.authorize(ctx, obj.ID, probo.ActionTrustCenterGet)
|
||||
if err != nil {
|
||||
// DarkLogo is the resolver for the darkLogo field.
|
||||
func (r *trustCenterResolver) DarkLogo(ctx context.Context, obj *types.TrustCenter) (*types.File, error) {
|
||||
if _, err := r.authorize(ctx, obj.ID, probo.ActionTrustCenterGet); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logoURL, err := r.probo.TrustCenters.GenerateDarkLogoURL(ctx, scope, obj.ID, 1*time.Hour)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot generate logo url", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
if obj.DarkLogo == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return logoURL, nil
|
||||
return r.loadFile(ctx, obj.DarkLogo.ID)
|
||||
}
|
||||
|
||||
// NdaFileURL is the resolver for the ndaFileUrl field.
|
||||
func (r *trustCenterResolver) NdaFileURL(ctx context.Context, obj *types.TrustCenter) (*string, error) {
|
||||
// Nda is the resolver for the nda field.
|
||||
func (r *trustCenterResolver) Nda(ctx context.Context, obj *types.TrustCenter) (*types.File, error) {
|
||||
hasPermission, err := r.Resolver.Permission(ctx, obj, probo.ActionTrustCenterGetNda)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot authorize", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
if !hasPermission {
|
||||
if !hasPermission || obj.Nda == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
scope := coredata.NewScopeFromObjectID(obj.ID)
|
||||
|
||||
fileURL, err := r.probo.TrustCenters.GenerateNDAFileURL(ctx, scope, obj.ID, 15*time.Minute)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot generate NDA file URL", log.Error(err))
|
||||
return nil, gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return fileURL, nil
|
||||
return r.loadFile(ctx, obj.Nda.ID)
|
||||
}
|
||||
|
||||
// Organization is the resolver for the organization field.
|
||||
@@ -1143,20 +1128,13 @@ func (r *trustCenterDocumentAccessConnectionResolver) TotalCount(ctx context.Con
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// FileURL is the resolver for the fileUrl field.
|
||||
func (r *trustCenterFileResolver) FileURL(ctx context.Context, obj *types.TrustCenterFile) (string, error) {
|
||||
scope, err := r.authorize(ctx, obj.ID, probo.ActionTrustCenterFileGetFileUrl)
|
||||
if err != nil {
|
||||
return "", err
|
||||
// File is the resolver for the file field.
|
||||
func (r *trustCenterFileResolver) File(ctx context.Context, obj *types.TrustCenterFile) (*types.File, error) {
|
||||
if _, err := r.authorize(ctx, obj.ID, probo.ActionTrustCenterFileGetFileUrl); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fileURL, err := r.probo.TrustCenterFiles.GenerateFileURL(ctx, scope, obj.ID, 1*time.Hour)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot generate file URL", log.Error(err))
|
||||
return "", gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return fileURL, nil
|
||||
return r.loadFile(ctx, obj.File.ID)
|
||||
}
|
||||
|
||||
// Organization is the resolver for the organization field.
|
||||
@@ -1207,20 +1185,13 @@ func (r *trustCenterFileConnectionResolver) TotalCount(ctx context.Context, obj
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// LogoURL is the resolver for the logoUrl field.
|
||||
func (r *trustCenterReferenceResolver) LogoURL(ctx context.Context, obj *types.TrustCenterReference) (string, error) {
|
||||
scope, err := r.authorize(ctx, obj.ID, probo.ActionTrustCenterReferenceGetLogoUrl)
|
||||
if err != nil {
|
||||
return "", err
|
||||
// Logo is the resolver for the logo field.
|
||||
func (r *trustCenterReferenceResolver) Logo(ctx context.Context, obj *types.TrustCenterReference) (*types.File, error) {
|
||||
if _, err := r.authorize(ctx, obj.ID, probo.ActionTrustCenterReferenceGetLogoUrl); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fileURL, err := r.probo.TrustCenterReferences.GenerateLogoURL(ctx, scope, obj.ID)
|
||||
if err != nil {
|
||||
r.logger.ErrorCtx(ctx, "cannot generate logo URL", log.Error(err))
|
||||
return "", gqlutils.Internal(ctx)
|
||||
}
|
||||
|
||||
return fileURL, nil
|
||||
return r.loadFile(ctx, obj.Logo.ID)
|
||||
}
|
||||
|
||||
// Permission is the resolver for the permission field.
|
||||
|
||||
@@ -34,13 +34,13 @@ type CommonThirdParty struct {
|
||||
TrustPageURL *string `json:"trustPageUrl,omitempty"`
|
||||
StatusPageURL *string `json:"statusPageUrl,omitempty"`
|
||||
TermsOfServiceURL *string `json:"termsOfServiceUrl,omitempty"`
|
||||
LogoFileID *gid.GID `json:"logoFileId,omitempty"`
|
||||
Logo *File `json:"logo,omitempty"`
|
||||
}
|
||||
|
||||
func (CommonThirdParty) IsTrackerPatternThirdPartyLink() {}
|
||||
|
||||
func NewCommonThirdParty(c *coredata.CommonThirdParty) *CommonThirdParty {
|
||||
return &CommonThirdParty{
|
||||
party := &CommonThirdParty{
|
||||
ID: c.ID,
|
||||
Name: c.Name,
|
||||
Category: c.Category,
|
||||
@@ -55,6 +55,11 @@ func NewCommonThirdParty(c *coredata.CommonThirdParty) *CommonThirdParty {
|
||||
TrustPageURL: c.TrustPageURL,
|
||||
StatusPageURL: c.StatusPageURL,
|
||||
TermsOfServiceURL: c.TermsOfServiceURL,
|
||||
LogoFileID: c.LogoFileID,
|
||||
}
|
||||
|
||||
if c.LogoFileID != nil {
|
||||
party.Logo = &File{ID: *c.LogoFileID}
|
||||
}
|
||||
|
||||
return party
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
)
|
||||
|
||||
func NewElectronicSignature(es *coredata.ElectronicSignature) *ElectronicSignature {
|
||||
return &ElectronicSignature{
|
||||
signature := &ElectronicSignature{
|
||||
ID: es.ID,
|
||||
Status: es.Status,
|
||||
DocumentType: es.DocumentType,
|
||||
@@ -29,6 +29,12 @@ func NewElectronicSignature(es *coredata.ElectronicSignature) *ElectronicSignatu
|
||||
CreatedAt: es.CreatedAt,
|
||||
UpdatedAt: es.UpdatedAt,
|
||||
}
|
||||
|
||||
if es.CertificateFileID != nil {
|
||||
signature.Certificate = &File{ID: *es.CertificateFileID}
|
||||
}
|
||||
|
||||
return signature
|
||||
}
|
||||
|
||||
func NewElectronicSignatureEvent(ev *coredata.ElectronicSignatureEvent) *ElectronicSignatureEvent {
|
||||
|
||||
@@ -17,17 +17,11 @@ package types
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/baseurl"
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/filemanager"
|
||||
)
|
||||
|
||||
func NewFile(r *coredata.File, base *baseurl.BaseURL) *File {
|
||||
var path string
|
||||
if r.Visibility == coredata.FileVisibilityPublic {
|
||||
path = "/api/files/v1/public/" + r.ID.String()
|
||||
} else {
|
||||
path = "/api/files/v1/" + r.ID.String()
|
||||
}
|
||||
|
||||
url := base.WithPath(path).MustString()
|
||||
url := base.WithPath(filemanager.DownloadAPIPath(r)).MustString()
|
||||
|
||||
return &File{
|
||||
ID: r.ID,
|
||||
|
||||
@@ -61,7 +61,7 @@ func NewFrameworkEdge(f *coredata.Framework, orderBy coredata.FrameworkOrderFiel
|
||||
}
|
||||
|
||||
func NewFramework(f *coredata.Framework) *Framework {
|
||||
return &Framework{
|
||||
framework := &Framework{
|
||||
ID: f.ID,
|
||||
Name: f.Name,
|
||||
Organization: &Organization{
|
||||
@@ -71,4 +71,14 @@ func NewFramework(f *coredata.Framework) *Framework {
|
||||
CreatedAt: f.CreatedAt,
|
||||
UpdatedAt: f.UpdatedAt,
|
||||
}
|
||||
|
||||
if f.LightLogoFileID != nil {
|
||||
framework.LightLogo = &File{ID: *f.LightLogoFileID}
|
||||
}
|
||||
|
||||
if f.DarkLogoFileID != nil {
|
||||
framework.DarkLogo = &File{ID: *f.DarkLogoFileID}
|
||||
}
|
||||
|
||||
return framework
|
||||
}
|
||||
|
||||
@@ -34,6 +34,14 @@ func NewOrganization(o *coredata.Organization) *Organization {
|
||||
UpdatedAt: o.UpdatedAt,
|
||||
}
|
||||
|
||||
if o.LogoFileID != nil {
|
||||
org.Logo = &File{ID: *o.LogoFileID}
|
||||
}
|
||||
|
||||
if o.HorizontalLogoFileID != nil {
|
||||
org.HorizontalLogo = &File{ID: *o.HorizontalLogoFileID}
|
||||
}
|
||||
|
||||
if o.CustomDomainID != nil {
|
||||
org.CustomDomain = &CustomDomain{
|
||||
ID: *o.CustomDomainID,
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
)
|
||||
|
||||
func NewThirdPartyBusinessAssociateAgreement(v *coredata.ThirdPartyBusinessAssociateAgreement, file *coredata.File) *ThirdPartyBusinessAssociateAgreement {
|
||||
func NewThirdPartyBusinessAssociateAgreement(v *coredata.ThirdPartyBusinessAssociateAgreement) *ThirdPartyBusinessAssociateAgreement {
|
||||
return &ThirdPartyBusinessAssociateAgreement{
|
||||
ID: v.ID,
|
||||
ThirdParty: &ThirdParty{
|
||||
@@ -26,8 +26,7 @@ func NewThirdPartyBusinessAssociateAgreement(v *coredata.ThirdPartyBusinessAssoc
|
||||
},
|
||||
ValidFrom: v.ValidFrom,
|
||||
ValidUntil: v.ValidUntil,
|
||||
FileName: file.FileName,
|
||||
FileSize: file.FileSize,
|
||||
File: &File{ID: v.FileID},
|
||||
CreatedAt: v.CreatedAt,
|
||||
UpdatedAt: v.UpdatedAt,
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
)
|
||||
|
||||
func NewThirdPartyDataPrivacyAgreement(v *coredata.ThirdPartyDataPrivacyAgreement, file *coredata.File) *ThirdPartyDataPrivacyAgreement {
|
||||
func NewThirdPartyDataPrivacyAgreement(v *coredata.ThirdPartyDataPrivacyAgreement) *ThirdPartyDataPrivacyAgreement {
|
||||
return &ThirdPartyDataPrivacyAgreement{
|
||||
ID: v.ID,
|
||||
ThirdParty: &ThirdParty{
|
||||
@@ -26,8 +26,7 @@ func NewThirdPartyDataPrivacyAgreement(v *coredata.ThirdPartyDataPrivacyAgreemen
|
||||
},
|
||||
ValidFrom: v.ValidFrom,
|
||||
ValidUntil: v.ValidUntil,
|
||||
FileName: file.FileName,
|
||||
FileSize: file.FileSize,
|
||||
File: &File{ID: v.FileID},
|
||||
CreatedAt: v.CreatedAt,
|
||||
UpdatedAt: v.UpdatedAt,
|
||||
}
|
||||
|
||||
@@ -25,10 +25,9 @@ type TrustCenter struct {
|
||||
ID gid.GID `json:"id"`
|
||||
Active bool `json:"active"`
|
||||
SearchEngineIndexing coredata.SearchEngineIndexing `json:"searchEngineIndexing"`
|
||||
LogoFileURL *string `json:"logoFileUrl,omitempty"`
|
||||
DarkLogoFileURL *string `json:"darkLogoFileUrl,omitempty"`
|
||||
NdaFileName *string `json:"ndaFileName,omitempty"`
|
||||
NdaFileURL *string `json:"ndaFileUrl,omitempty"`
|
||||
Logo *File `json:"logo,omitempty"`
|
||||
DarkLogo *File `json:"darkLogo,omitempty"`
|
||||
Nda *File `json:"nda,omitempty"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
Organization *Organization `json:"organization"`
|
||||
@@ -43,21 +42,29 @@ type TrustCenter struct {
|
||||
func (TrustCenter) IsNode() {}
|
||||
func (t TrustCenter) GetID() gid.GID { return t.ID }
|
||||
|
||||
func NewTrustCenter(tc *coredata.TrustCenter, file *coredata.File) *TrustCenter {
|
||||
var ndaFileName *string
|
||||
if file != nil {
|
||||
ndaFileName = &file.FileName
|
||||
}
|
||||
|
||||
return &TrustCenter{
|
||||
func NewTrustCenter(tc *coredata.TrustCenter) *TrustCenter {
|
||||
trustCenter := &TrustCenter{
|
||||
ID: tc.ID,
|
||||
Organization: &Organization{
|
||||
ID: tc.OrganizationID,
|
||||
},
|
||||
Active: tc.Active,
|
||||
SearchEngineIndexing: tc.SearchEngineIndexing,
|
||||
NdaFileName: ndaFileName,
|
||||
CreatedAt: tc.CreatedAt,
|
||||
UpdatedAt: tc.UpdatedAt,
|
||||
}
|
||||
|
||||
if tc.LogoFileID != nil {
|
||||
trustCenter.Logo = &File{ID: *tc.LogoFileID}
|
||||
}
|
||||
|
||||
if tc.DarkLogoFileID != nil {
|
||||
trustCenter.DarkLogo = &File{ID: *tc.DarkLogoFileID}
|
||||
}
|
||||
|
||||
if tc.NonDisclosureAgreementFileID != nil {
|
||||
trustCenter.Nda = &File{ID: *tc.NonDisclosureAgreementFileID}
|
||||
}
|
||||
|
||||
return trustCenter
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ func NewTrustCenterFile(tcf *coredata.TrustCenterFile) *TrustCenterFile {
|
||||
ID: tcf.ID,
|
||||
Name: tcf.Name,
|
||||
Category: tcf.Category,
|
||||
File: &File{ID: tcf.FileID},
|
||||
TrustCenterVisibility: tcf.TrustCenterVisibility,
|
||||
CreatedAt: tcf.CreatedAt,
|
||||
UpdatedAt: tcf.UpdatedAt,
|
||||
|
||||
@@ -35,6 +35,7 @@ func NewTrustCenterReference(tcc *coredata.TrustCenterReference) *TrustCenterRef
|
||||
Name: tcc.Name,
|
||||
Description: tcc.Description,
|
||||
WebsiteURL: tcc.WebsiteURL,
|
||||
Logo: &File{ID: tcc.LogoFileID},
|
||||
Rank: tcc.Rank,
|
||||
CreatedAt: tcc.CreatedAt,
|
||||
UpdatedAt: tcc.UpdatedAt,
|
||||
|
||||
Reference in New Issue
Block a user