diff --git a/apps/console/src/__generated__/core/CompliancePageBrandPageQuery.graphql.ts b/apps/console/src/__generated__/core/CompliancePageBrandPageQuery.graphql.ts index 3e948af62..947f441ff 100644 --- a/apps/console/src/__generated__/core/CompliancePageBrandPageQuery.graphql.ts +++ b/apps/console/src/__generated__/core/CompliancePageBrandPageQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<<6ae7a5c203ff61690408ec2753fe2f11>> * @lightSyntaxTransform * @nogrep */ @@ -14,10 +14,17 @@ export type CompliancePageBrandPageQuery$variables = { }; export type CompliancePageBrandPageQuery$data = { readonly organization: { - readonly compliancePage?: { + readonly __typename: "Organization"; + readonly compliancePage: { + readonly canUpdate: boolean; readonly darkLogoFileUrl: string | null | undefined; + readonly id: string; readonly logoFileUrl: string | null | undefined; - } | null | undefined; + }; + } | { + // This will never be '%other', but we need some + // value in case none of the concrete values match. + readonly __typename: "%other"; }; }; export type CompliancePageBrandPageQuery = { @@ -44,21 +51,53 @@ v2 = { "alias": null, "args": null, "kind": "ScalarField", - "name": "logoFileUrl", + "name": "__typename", "storageKey": null }, v3 = { "alias": null, "args": null, "kind": "ScalarField", - "name": "darkLogoFileUrl", + "name": "id", "storageKey": null }, v4 = { - "alias": null, + "alias": "compliancePage", "args": null, - "kind": "ScalarField", - "name": "id", + "concreteType": "TrustCenter", + "kind": "LinkedField", + "name": "trustCenter", + "plural": false, + "selections": [ + (v3/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "logoFileUrl", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "darkLogoFileUrl", + "storageKey": null + }, + { + "alias": "canUpdate", + "args": [ + { + "kind": "Literal", + "name": "action", + "value": "core:trust-center:update" + } + ], + "kind": "ScalarField", + "name": "permission", + "storageKey": "permission(action:\"core:trust-center:update\")" + } + ], "storageKey": null }; return { @@ -76,21 +115,14 @@ return { "name": "node", "plural": false, "selections": [ + (v2/*: any*/), { "kind": "InlineFragment", "selections": [ { - "alias": "compliancePage", - "args": null, - "concreteType": "TrustCenter", - "kind": "LinkedField", - "name": "trustCenter", - "plural": false, - "selections": [ - (v2/*: any*/), - (v3/*: any*/) - ], - "storageKey": null + "kind": "RequiredField", + "field": (v4/*: any*/), + "action": "THROW" } ], "type": "Organization", @@ -117,51 +149,32 @@ return { "name": "node", "plural": false, "selections": [ - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "__typename", - "storageKey": null - }, + (v2/*: any*/), { "kind": "InlineFragment", "selections": [ - { - "alias": "compliancePage", - "args": null, - "concreteType": "TrustCenter", - "kind": "LinkedField", - "name": "trustCenter", - "plural": false, - "selections": [ - (v2/*: any*/), - (v3/*: any*/), - (v4/*: any*/) - ], - "storageKey": null - } + (v4/*: any*/) ], "type": "Organization", "abstractKey": null }, - (v4/*: any*/) + (v3/*: any*/) ], "storageKey": null } ] }, "params": { - "cacheID": "17e4be042ed0e09a4b299d38f33dff32", + "cacheID": "a0e24c6bb927ff34274492952df0346b", "id": null, "metadata": {}, "name": "CompliancePageBrandPageQuery", "operationKind": "query", - "text": "query CompliancePageBrandPageQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n compliancePage: trustCenter {\n logoFileUrl\n darkLogoFileUrl\n id\n }\n }\n id\n }\n}\n" + "text": "query CompliancePageBrandPageQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n compliancePage: trustCenter {\n id\n logoFileUrl\n darkLogoFileUrl\n canUpdate: permission(action: \"core:trust-center:update\")\n }\n }\n id\n }\n}\n" } }; })(); -(node as any).hash = "3364d36a8dd77d39d00d50f451a027a7"; +(node as any).hash = "3d2d6ef82bb9cf232aad738c906b8a54"; export default node; diff --git a/apps/console/src/__generated__/core/CompliancePageBrandPage_updateMutation.graphql.ts b/apps/console/src/__generated__/core/CompliancePageBrandPage_updateMutation.graphql.ts new file mode 100644 index 000000000..554843904 --- /dev/null +++ b/apps/console/src/__generated__/core/CompliancePageBrandPage_updateMutation.graphql.ts @@ -0,0 +1,123 @@ +/** + * @generated SignedSource<> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +export type UpdateTrustCenterBrandInput = { + darkLogoFile?: any | null | undefined; + logoFile?: any | null | undefined; + trustCenterId: string; +}; +export type CompliancePageBrandPage_updateMutation$variables = { + input: UpdateTrustCenterBrandInput; +}; +export type CompliancePageBrandPage_updateMutation$data = { + readonly updateTrustCenterBrand: { + readonly trustCenter: { + readonly darkLogoFileUrl: string | null | undefined; + readonly id: string; + readonly logoFileUrl: string | null | undefined; + }; + }; +}; +export type CompliancePageBrandPage_updateMutation = { + response: CompliancePageBrandPage_updateMutation$data; + variables: CompliancePageBrandPage_updateMutation$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "input" + } +], +v1 = [ + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "input", + "variableName": "input" + } + ], + "concreteType": "UpdateTrustCenterBrandPayload", + "kind": "LinkedField", + "name": "updateTrustCenterBrand", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "TrustCenter", + "kind": "LinkedField", + "name": "trustCenter", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "logoFileUrl", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "darkLogoFileUrl", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "CompliancePageBrandPage_updateMutation", + "selections": (v1/*: any*/), + "type": "Mutation", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "CompliancePageBrandPage_updateMutation", + "selections": (v1/*: any*/) + }, + "params": { + "cacheID": "fb398aa8a46a93ca7b68e4ef9841a4d6", + "id": null, + "metadata": {}, + "name": "CompliancePageBrandPage_updateMutation", + "operationKind": "mutation", + "text": "mutation CompliancePageBrandPage_updateMutation(\n $input: UpdateTrustCenterBrandInput!\n) {\n updateTrustCenterBrand(input: $input) {\n trustCenter {\n id\n logoFileUrl\n darkLogoFileUrl\n }\n }\n}\n" + } +}; +})(); + +(node as any).hash = "cb158c24dbd3824e6fa9cc3e4632dc81"; + +export default node; diff --git a/apps/console/src/pages/organizations/compliance-page/brand/CompliancePageBrandPage.tsx b/apps/console/src/pages/organizations/compliance-page/brand/CompliancePageBrandPage.tsx index 923f89c8a..ab434b53b 100644 --- a/apps/console/src/pages/organizations/compliance-page/brand/CompliancePageBrandPage.tsx +++ b/apps/console/src/pages/organizations/compliance-page/brand/CompliancePageBrandPage.tsx @@ -1,25 +1,312 @@ +import { useTranslate } from "@probo/i18n"; +import { + Button, + Card, + Dropzone, + FileButton, + IconTrashCan, + Label, + Spinner, +} from "@probo/ui"; +import { type ChangeEventHandler, useState } from "react"; import { type PreloadedQuery, usePreloadedQuery } from "react-relay"; import { graphql } from "relay-runtime"; +import type { CompliancePageBrandPage_updateMutation } from "#/__generated__/core/CompliancePageBrandPage_updateMutation.graphql"; import type { CompliancePageBrandPageQuery } from "#/__generated__/core/CompliancePageBrandPageQuery.graphql"; +import { useMutationWithToasts } from "#/hooks/useMutationWithToasts"; export const compliancePageBrandPageQuery = graphql` query CompliancePageBrandPageQuery($organizationId: ID!) { organization: node(id: $organizationId) { + __typename ... on Organization { - compliancePage: trustCenter { + compliancePage: trustCenter @required(action: THROW) { + id logoFileUrl darkLogoFileUrl + canUpdate: permission(action: "core:trust-center:update") } } } } `; +const updateTrustCenterBrandMutation = graphql` + mutation CompliancePageBrandPage_updateMutation($input: UpdateTrustCenterBrandInput!) { + updateTrustCenterBrand(input: $input) { + trustCenter { + id + logoFileUrl + darkLogoFileUrl + } + } + } +`; + export function CompliancePageBrandPage(props: { queryRef: PreloadedQuery }) { const { queryRef } = props; + const { __ } = useTranslate(); const { organization } = usePreloadedQuery(compliancePageBrandPageQuery, queryRef); + if (organization.__typename !== "Organization") { + throw new Error("invalid type for node"); + } - return
{JSON.stringify(organization, null, 2)}
; + const trustCenterId = organization.compliancePage.id; + const logoFileUrl = organization.compliancePage.logoFileUrl; + const darkLogoFileUrl = organization.compliancePage.darkLogoFileUrl; + + const [logoPreview, setLogoPreview] = useState(null); + const [darkLogoPreview, setDarkLogoPreview] = useState(null); + + const [updateBrand, isUpdating] = useMutationWithToasts( + updateTrustCenterBrandMutation, + { + successMessage: __("Compliance page branding updated successfully"), + errorMessage: __("Failed to update compliance page branding"), + }, + ); + const disabled = isUpdating || !organization.compliancePage.canUpdate; + + const processLogoFile = (file: File, setPreview: (url: string) => void) => { + const reader = new FileReader(); + reader.onload = () => { + setPreview(reader.result as string); + }; + reader.readAsDataURL(file); + }; + + const handleLogoChange: ChangeEventHandler = (e) => { + const file = e.target.files?.[0]; + if (!file) return; + + processLogoFile(file, setLogoPreview); + + void updateBrand({ + variables: { + input: { + trustCenterId, + logoFile: null, + }, + }, + uploadables: { + "input.logoFile": file, + }, + onCompleted: () => { + setLogoPreview(null); + }, + }); + }; + + const handleDarkLogoChange: ChangeEventHandler = (e) => { + const file = e.target.files?.[0]; + if (!file) return; + + processLogoFile(file, setDarkLogoPreview); + + void updateBrand({ + variables: { + input: { + trustCenterId, + darkLogoFile: null, + }, + }, + uploadables: { + "input.darkLogoFile": file, + }, + onCompleted: () => { + setDarkLogoPreview(null); + }, + }); + }; + + const handleLogoDrop = (files: File[]) => { + const file = files[0]; + if (!file) return; + + processLogoFile(file, setLogoPreview); + + void updateBrand({ + variables: { + input: { + trustCenterId, + logoFile: null, + }, + }, + uploadables: { + "input.logoFile": file, + }, + onCompleted: () => { + setLogoPreview(null); + }, + }); + }; + + const handleDarkLogoDrop = (files: File[]) => { + const file = files[0]; + if (!file) return; + + processLogoFile(file, setDarkLogoPreview); + + void updateBrand({ + variables: { + input: { + trustCenterId, + darkLogoFile: null, + }, + }, + uploadables: { + "input.darkLogoFile": file, + }, + onCompleted: () => { + setDarkLogoPreview(null); + }, + }); + }; + + const handleRemoveLogo = async () => { + await updateBrand({ + variables: { + input: { + trustCenterId, + logoFile: null, + }, + }, + onSuccess: () => { + setLogoPreview(null); + }, + }); + }; + + const handleRemoveDarkLogo = async () => { + await updateBrand({ + variables: { + input: { + trustCenterId, + darkLogoFile: null, + }, + }, + onSuccess: () => { + setDarkLogoPreview(null); + }, + }); + }; + + const currentLogoUrl = logoPreview || logoFileUrl; + const currentDarkLogoUrl = darkLogoPreview || darkLogoFileUrl; + + return ( +
+
+

{__("Branding")}

+ {isUpdating && } +
+ + +
+
+ +

+ {__("This logo will be displayed on your public compliance page.")} +

+ + {currentLogoUrl + ? ( +
+
+ {__("Compliance +
+ + {isUpdating ? __("Uploading...") : __("Change logo")} + +
+ ) + : ( + + )} +
+
+ +

+ {__("This logo will be used when dark mode is enabled.")} +

+ + {currentDarkLogoUrl + ? ( +
+
+ {__("Compliance +
+ + {isUpdating ? __("Uploading...") : __("Change dark logo")} + +
+ ) + : ( + + )} +
+
+
+
+ ); } diff --git a/pkg/probo/trust_center_service.go b/pkg/probo/trust_center_service.go index 458693c93..561d8c803 100644 --- a/pkg/probo/trust_center_service.go +++ b/pkg/probo/trust_center_service.go @@ -48,6 +48,12 @@ type ( File io.Reader FileName string } + + UpdateTrustCenterBrandRequest struct { + TrustCenterID gid.GID + LogoFile **FileUpload + DarkLogoFile **FileUpload + } ) func (utcr *UpdateTrustCenterRequest) Validate() error { @@ -302,6 +308,132 @@ func (s TrustCenterService) DeleteNDA( return trustCenter, nil, nil } +func (s TrustCenterService) UpdateTrustCenterBrand( + ctx context.Context, + req *UpdateTrustCenterBrandRequest, +) (*coredata.TrustCenter, *coredata.File, error) { + var trustCenter *coredata.TrustCenter + var ndaFile *coredata.File + + err := s.svc.pg.WithTx( + ctx, + func(conn pg.Conn) error { + trustCenter = &coredata.TrustCenter{} + if err := trustCenter.LoadByID(ctx, conn, s.svc.scope, req.TrustCenterID); err != nil { + return fmt.Errorf("cannot load trust center: %w", err) + } + + now := time.Now() + + if req.LogoFile != nil { + if *req.LogoFile == nil { + trustCenter.LogoFileID = nil + } else { + file, err := s.uploadFile(ctx, conn, *req.LogoFile, "trust-center-logo", trustCenter) + if err != nil { + return fmt.Errorf("cannot upload logo file: %w", err) + } + trustCenter.LogoFileID = &file.ID + } + } + + if req.DarkLogoFile != nil { + if *req.DarkLogoFile == nil { + trustCenter.DarkLogoFileID = nil + } else { + file, err := s.uploadFile(ctx, conn, *req.DarkLogoFile, "trust-center-dark-logo", trustCenter) + if err != nil { + return fmt.Errorf("cannot upload dark logo file: %w", err) + } + trustCenter.DarkLogoFileID = &file.ID + } + } + + trustCenter.UpdatedAt = now + + if err := trustCenter.Update(ctx, conn, s.svc.scope); err != nil { + return fmt.Errorf("cannot update trust center: %w", err) + } + + if trustCenter.NonDisclosureAgreementFileID != nil { + ndaFile = &coredata.File{} + if err := ndaFile.LoadByID(ctx, conn, s.svc.scope, *trustCenter.NonDisclosureAgreementFileID); err != nil { + return fmt.Errorf("cannot load nda file: %w", err) + } + } + + return nil + }, + ) + + if err != nil { + return nil, nil, err + } + + return trustCenter, ndaFile, nil +} + +func (s TrustCenterService) uploadFile( + ctx context.Context, + conn pg.Conn, + fileUpload *FileUpload, + fileType string, + trustCenter *coredata.TrustCenter, +) (*coredata.File, error) { + objectKey, err := uuid.NewV7() + if err != nil { + return nil, fmt.Errorf("cannot generate object key: %w", err) + } + + mimeType := fileUpload.ContentType + if mimeType == "" { + mimeType = mime.TypeByExtension(filepath.Ext(fileUpload.Filename)) + } + + _, err = s.svc.s3.PutObject(ctx, &s3.PutObjectInput{ + Bucket: &s.svc.bucket, + Key: aws.String(objectKey.String()), + Body: fileUpload.Content, + ContentType: &mimeType, + Metadata: map[string]string{ + "type": fileType, + "trust-center-id": trustCenter.ID.String(), + "organization-id": trustCenter.OrganizationID.String(), + }, + }) + 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) + + file := &coredata.File{ + ID: fileID, + BucketName: s.svc.bucket, + MimeType: mimeType, + FileName: fileUpload.Filename, + FileKey: objectKey.String(), + FileSize: *headOutput.ContentLength, + CreatedAt: now, + UpdatedAt: now, + } + + if err := file.Insert(ctx, conn, s.svc.scope); err != nil { + return nil, fmt.Errorf("cannot insert file: %w", err) + } + + return file, nil +} + func (s TrustCenterService) GenerateNDAFileURL( ctx context.Context, trustCenterID gid.GID, @@ -421,7 +553,7 @@ func (s TrustCenterService) GenerateDarkLogoURL( return nil, err } - if compliancePage.LogoFileID == nil { + if compliancePage.DarkLogoFileID == nil { return nil, nil } diff --git a/pkg/server/api/console/v1/schema.graphql b/pkg/server/api/console/v1/schema.graphql index f05f1f226..3761e1594 100644 --- a/pkg/server/api/console/v1/schema.graphql +++ b/pkg/server/api/console/v1/schema.graphql @@ -3071,6 +3071,9 @@ type Mutation { deleteTrustCenterNDA( input: DeleteTrustCenterNDAInput! ): DeleteTrustCenterNDAPayload! + updateTrustCenterBrand( + input: UpdateTrustCenterBrandInput! + ): UpdateTrustCenterBrandPayload! # Trust Center Access CRUD mutations createTrustCenterAccess( input: CreateTrustCenterAccessInput! @@ -3445,6 +3448,12 @@ input DeleteTrustCenterNDAInput { trustCenterId: ID! } +input UpdateTrustCenterBrandInput { + trustCenterId: ID! + logoFile: Upload @goField(omittable: true) + darkLogoFile: Upload @goField(omittable: true) +} + input CreateTrustCenterAccessInput { trustCenterId: ID! email: EmailAddr! @@ -4308,6 +4317,10 @@ type DeleteTrustCenterNDAPayload { trustCenter: TrustCenter! } +type UpdateTrustCenterBrandPayload { + trustCenter: TrustCenter! +} + type CreateTrustCenterAccessPayload { trustCenterAccessEdge: TrustCenterAccessEdge! } diff --git a/pkg/server/api/console/v1/schema/schema.go b/pkg/server/api/console/v1/schema/schema.go index 8cb9c5fc8..36775b084 100644 --- a/pkg/server/api/console/v1/schema/schema.go +++ b/pkg/server/api/console/v1/schema/schema.go @@ -1063,6 +1063,7 @@ type ComplexityRoot struct { UpdateTransferImpactAssessment func(childComplexity int, input types.UpdateTransferImpactAssessmentInput) int UpdateTrustCenter func(childComplexity int, input types.UpdateTrustCenterInput) int UpdateTrustCenterAccess func(childComplexity int, input types.UpdateTrustCenterAccessInput) int + UpdateTrustCenterBrand func(childComplexity int, input types.UpdateTrustCenterBrandInput) int UpdateTrustCenterFile func(childComplexity int, input types.UpdateTrustCenterFileInput) int UpdateTrustCenterReference func(childComplexity int, input types.UpdateTrustCenterReferenceInput) int UpdateVendor func(childComplexity int, input types.UpdateVendorInput) int @@ -1701,6 +1702,10 @@ type ComplexityRoot struct { TrustCenterAccess func(childComplexity int) int } + UpdateTrustCenterBrandPayload struct { + TrustCenter func(childComplexity int) int + } + UpdateTrustCenterFilePayload struct { TrustCenterFile func(childComplexity int) int } @@ -2089,6 +2094,7 @@ type MutationResolver interface { UpdateTrustCenter(ctx context.Context, input types.UpdateTrustCenterInput) (*types.UpdateTrustCenterPayload, error) UploadTrustCenterNda(ctx context.Context, input types.UploadTrustCenterNDAInput) (*types.UploadTrustCenterNDAPayload, error) DeleteTrustCenterNda(ctx context.Context, input types.DeleteTrustCenterNDAInput) (*types.DeleteTrustCenterNDAPayload, error) + UpdateTrustCenterBrand(ctx context.Context, input types.UpdateTrustCenterBrandInput) (*types.UpdateTrustCenterBrandPayload, error) CreateTrustCenterAccess(ctx context.Context, input types.CreateTrustCenterAccessInput) (*types.CreateTrustCenterAccessPayload, error) UpdateTrustCenterAccess(ctx context.Context, input types.UpdateTrustCenterAccessInput) (*types.UpdateTrustCenterAccessPayload, error) DeleteTrustCenterAccess(ctx context.Context, input types.DeleteTrustCenterAccessInput) (*types.DeleteTrustCenterAccessPayload, error) @@ -6457,6 +6463,17 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin } return e.complexity.Mutation.UpdateTrustCenterAccess(childComplexity, args["input"].(types.UpdateTrustCenterAccessInput)), true + case "Mutation.updateTrustCenterBrand": + if e.complexity.Mutation.UpdateTrustCenterBrand == nil { + break + } + + args, err := ec.field_Mutation_updateTrustCenterBrand_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.UpdateTrustCenterBrand(childComplexity, args["input"].(types.UpdateTrustCenterBrandInput)), true case "Mutation.updateTrustCenterFile": if e.complexity.Mutation.UpdateTrustCenterFile == nil { break @@ -9188,6 +9205,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.UpdateTrustCenterAccessPayload.TrustCenterAccess(childComplexity), true + case "UpdateTrustCenterBrandPayload.trustCenter": + if e.complexity.UpdateTrustCenterBrandPayload.TrustCenter == nil { + break + } + + return e.complexity.UpdateTrustCenterBrandPayload.TrustCenter(childComplexity), true + case "UpdateTrustCenterFilePayload.trustCenterFile": if e.complexity.UpdateTrustCenterFilePayload.TrustCenterFile == nil { break @@ -10209,6 +10233,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputUpdateTaskInput, ec.unmarshalInputUpdateTransferImpactAssessmentInput, ec.unmarshalInputUpdateTrustCenterAccessInput, + ec.unmarshalInputUpdateTrustCenterBrandInput, ec.unmarshalInputUpdateTrustCenterFileInput, ec.unmarshalInputUpdateTrustCenterInput, ec.unmarshalInputUpdateTrustCenterReferenceInput, @@ -13399,6 +13424,9 @@ type Mutation { deleteTrustCenterNDA( input: DeleteTrustCenterNDAInput! ): DeleteTrustCenterNDAPayload! + updateTrustCenterBrand( + input: UpdateTrustCenterBrandInput! + ): UpdateTrustCenterBrandPayload! # Trust Center Access CRUD mutations createTrustCenterAccess( input: CreateTrustCenterAccessInput! @@ -13773,6 +13801,12 @@ input DeleteTrustCenterNDAInput { trustCenterId: ID! } +input UpdateTrustCenterBrandInput { + trustCenterId: ID! + logoFile: Upload @goField(omittable: true) + darkLogoFile: Upload @goField(omittable: true) +} + input CreateTrustCenterAccessInput { trustCenterId: ID! email: EmailAddr! @@ -14636,6 +14670,10 @@ type DeleteTrustCenterNDAPayload { trustCenter: TrustCenter! } +type UpdateTrustCenterBrandPayload { + trustCenter: TrustCenter! +} + type CreateTrustCenterAccessPayload { trustCenterAccessEdge: TrustCenterAccessEdge! } @@ -17681,6 +17719,17 @@ func (ec *executionContext) field_Mutation_updateTrustCenterAccess_args(ctx cont return args, nil } +func (ec *executionContext) field_Mutation_updateTrustCenterBrand_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "input", ec.unmarshalNUpdateTrustCenterBrandInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐUpdateTrustCenterBrandInput) + if err != nil { + return nil, err + } + args["input"] = arg0 + return args, nil +} + func (ec *executionContext) field_Mutation_updateTrustCenterFile_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -33518,6 +33567,51 @@ func (ec *executionContext) fieldContext_Mutation_deleteTrustCenterNDA(ctx conte return fc, nil } +func (ec *executionContext) _Mutation_updateTrustCenterBrand(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_Mutation_updateTrustCenterBrand, + func(ctx context.Context) (any, error) { + fc := graphql.GetFieldContext(ctx) + return ec.resolvers.Mutation().UpdateTrustCenterBrand(ctx, fc.Args["input"].(types.UpdateTrustCenterBrandInput)) + }, + nil, + ec.marshalNUpdateTrustCenterBrandPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐUpdateTrustCenterBrandPayload, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_Mutation_updateTrustCenterBrand(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "trustCenter": + return ec.fieldContext_UpdateTrustCenterBrandPayload_trustCenter(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type UpdateTrustCenterBrandPayload", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_updateTrustCenterBrand_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Mutation_createTrustCenterAccess(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -54304,6 +54398,61 @@ func (ec *executionContext) fieldContext_UpdateTrustCenterAccessPayload_trustCen return fc, nil } +func (ec *executionContext) _UpdateTrustCenterBrandPayload_trustCenter(ctx context.Context, field graphql.CollectedField, obj *types.UpdateTrustCenterBrandPayload) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_UpdateTrustCenterBrandPayload_trustCenter, + func(ctx context.Context) (any, error) { + return obj.TrustCenter, nil + }, + nil, + ec.marshalNTrustCenter2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐTrustCenter, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_UpdateTrustCenterBrandPayload_trustCenter(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "UpdateTrustCenterBrandPayload", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_TrustCenter_id(ctx, field) + case "active": + return ec.fieldContext_TrustCenter_active(ctx, field) + case "logoFileUrl": + return ec.fieldContext_TrustCenter_logoFileUrl(ctx, field) + case "darkLogoFileUrl": + return ec.fieldContext_TrustCenter_darkLogoFileUrl(ctx, field) + case "ndaFileName": + return ec.fieldContext_TrustCenter_ndaFileName(ctx, field) + case "ndaFileUrl": + return ec.fieldContext_TrustCenter_ndaFileUrl(ctx, field) + case "createdAt": + return ec.fieldContext_TrustCenter_createdAt(ctx, field) + case "updatedAt": + return ec.fieldContext_TrustCenter_updatedAt(ctx, field) + case "organization": + return ec.fieldContext_TrustCenter_organization(ctx, field) + case "accesses": + return ec.fieldContext_TrustCenter_accesses(ctx, field) + case "references": + return ec.fieldContext_TrustCenter_references(ctx, field) + case "permission": + return ec.fieldContext_TrustCenter_permission(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type TrustCenter", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _UpdateTrustCenterFilePayload_trustCenterFile(ctx context.Context, field graphql.CollectedField, obj *types.UpdateTrustCenterFilePayload) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -67961,6 +68110,47 @@ func (ec *executionContext) unmarshalInputUpdateTrustCenterAccessInput(ctx conte return it, nil } +func (ec *executionContext) unmarshalInputUpdateTrustCenterBrandInput(ctx context.Context, obj any) (types.UpdateTrustCenterBrandInput, error) { + var it types.UpdateTrustCenterBrandInput + asMap := map[string]any{} + for k, v := range obj.(map[string]any) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"trustCenterId", "logoFile", "darkLogoFile"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "trustCenterId": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("trustCenterId")) + data, err := ec.unmarshalNID2goᚗproboᚗincᚋproboᚋpkgᚋgidᚐGID(ctx, v) + if err != nil { + return it, err + } + it.TrustCenterID = data + case "logoFile": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("logoFile")) + data, err := ec.unmarshalOUpload2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx, v) + if err != nil { + return it, err + } + it.LogoFile = graphql.OmittableOf(data) + case "darkLogoFile": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("darkLogoFile")) + data, err := ec.unmarshalOUpload2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚐUpload(ctx, v) + if err != nil { + return it, err + } + it.DarkLogoFile = graphql.OmittableOf(data) + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputUpdateTrustCenterFileInput(ctx context.Context, obj any) (types.UpdateTrustCenterFileInput, error) { var it types.UpdateTrustCenterFileInput asMap := map[string]any{} @@ -78429,6 +78619,13 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { out.Invalids++ } + case "updateTrustCenterBrand": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_updateTrustCenterBrand(ctx, field) + }) + if out.Values[i] == graphql.Null { + out.Invalids++ + } case "createTrustCenterAccess": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { return ec._Mutation_createTrustCenterAccess(ctx, field) @@ -87121,6 +87318,45 @@ func (ec *executionContext) _UpdateTrustCenterAccessPayload(ctx context.Context, return out } +var updateTrustCenterBrandPayloadImplementors = []string{"UpdateTrustCenterBrandPayload"} + +func (ec *executionContext) _UpdateTrustCenterBrandPayload(ctx context.Context, sel ast.SelectionSet, obj *types.UpdateTrustCenterBrandPayload) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, updateTrustCenterBrandPayloadImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("UpdateTrustCenterBrandPayload") + case "trustCenter": + out.Values[i] = ec._UpdateTrustCenterBrandPayload_trustCenter(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var updateTrustCenterFilePayloadImplementors = []string{"UpdateTrustCenterFilePayload"} func (ec *executionContext) _UpdateTrustCenterFilePayload(ctx context.Context, sel ast.SelectionSet, obj *types.UpdateTrustCenterFilePayload) graphql.Marshaler { @@ -97541,6 +97777,25 @@ func (ec *executionContext) marshalNUpdateTrustCenterAccessPayload2ᚖgoᚗprobo return ec._UpdateTrustCenterAccessPayload(ctx, sel, v) } +func (ec *executionContext) unmarshalNUpdateTrustCenterBrandInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐUpdateTrustCenterBrandInput(ctx context.Context, v any) (types.UpdateTrustCenterBrandInput, error) { + res, err := ec.unmarshalInputUpdateTrustCenterBrandInput(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNUpdateTrustCenterBrandPayload2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐUpdateTrustCenterBrandPayload(ctx context.Context, sel ast.SelectionSet, v types.UpdateTrustCenterBrandPayload) graphql.Marshaler { + return ec._UpdateTrustCenterBrandPayload(ctx, sel, &v) +} + +func (ec *executionContext) marshalNUpdateTrustCenterBrandPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐUpdateTrustCenterBrandPayload(ctx context.Context, sel ast.SelectionSet, v *types.UpdateTrustCenterBrandPayload) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._UpdateTrustCenterBrandPayload(ctx, sel, v) +} + func (ec *executionContext) unmarshalNUpdateTrustCenterFileInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐUpdateTrustCenterFileInput(ctx context.Context, v any) (types.UpdateTrustCenterFileInput, error) { res, err := ec.unmarshalInputUpdateTrustCenterFileInput(ctx, v) return res, graphql.ErrorOnPath(ctx, err) diff --git a/pkg/server/api/console/v1/types/types.go b/pkg/server/api/console/v1/types/types.go index e23f62629..d092d1da6 100644 --- a/pkg/server/api/console/v1/types/types.go +++ b/pkg/server/api/console/v1/types/types.go @@ -2266,6 +2266,16 @@ type UpdateTrustCenterAccessPayload struct { TrustCenterAccess *TrustCenterAccess `json:"trustCenterAccess"` } +type UpdateTrustCenterBrandInput struct { + TrustCenterID gid.GID `json:"trustCenterId"` + LogoFile graphql.Omittable[*graphql.Upload] `json:"logoFile,omitempty"` + DarkLogoFile graphql.Omittable[*graphql.Upload] `json:"darkLogoFile,omitempty"` +} + +type UpdateTrustCenterBrandPayload struct { + TrustCenter *TrustCenter `json:"trustCenter"` +} + type UpdateTrustCenterFileInput struct { ID gid.GID `json:"id"` Name *string `json:"name,omitempty"` diff --git a/pkg/server/api/console/v1/v1_resolver.go b/pkg/server/api/console/v1/v1_resolver.go index ede486b55..e08643c90 100644 --- a/pkg/server/api/console/v1/v1_resolver.go +++ b/pkg/server/api/console/v1/v1_resolver.go @@ -1789,6 +1789,61 @@ func (r *mutationResolver) DeleteTrustCenterNda(ctx context.Context, input types }, nil } +// UpdateTrustCenterBrand is the resolver for the updateTrustCenterBrand field. +func (r *mutationResolver) UpdateTrustCenterBrand(ctx context.Context, input types.UpdateTrustCenterBrandInput) (*types.UpdateTrustCenterBrandPayload, error) { + if err := r.authorize(ctx, input.TrustCenterID, probo.ActionTrustCenterUpdate); err != nil { + return nil, err + } + + prb := r.ProboService(ctx, input.TrustCenterID.TenantID()) + + req := &probo.UpdateTrustCenterBrandRequest{ + TrustCenterID: input.TrustCenterID, + } + + if input.LogoFile.IsSet() { + logoFile := input.LogoFile.Value() + if logoFile == nil { + var nilFile *probo.FileUpload + req.LogoFile = &nilFile + } else { + fileUpload := &probo.FileUpload{ + Content: logoFile.File, + Filename: logoFile.Filename, + Size: logoFile.Size, + ContentType: logoFile.ContentType, + } + req.LogoFile = &fileUpload + } + } + + if input.DarkLogoFile.IsSet() { + darkLogoFile := input.DarkLogoFile.Value() + if darkLogoFile == nil { + var nilFile *probo.FileUpload + req.DarkLogoFile = &nilFile + } else { + fileUpload := &probo.FileUpload{ + Content: darkLogoFile.File, + Filename: darkLogoFile.Filename, + Size: darkLogoFile.Size, + ContentType: darkLogoFile.ContentType, + } + req.DarkLogoFile = &fileUpload + } + } + + trustCenter, file, err := prb.TrustCenters.UpdateTrustCenterBrand(ctx, req) + if err != nil { + r.logger.ErrorCtx(ctx, "cannot update trust center brand", log.Error(err)) + return nil, gqlutils.Internal(ctx) + } + + return &types.UpdateTrustCenterBrandPayload{ + TrustCenter: types.NewTrustCenter(trustCenter, file), + }, nil +} + // CreateTrustCenterAccess is the resolver for the createTrustCenterAccess field. func (r *mutationResolver) CreateTrustCenterAccess(ctx context.Context, input types.CreateTrustCenterAccessInput) (*types.CreateTrustCenterAccessPayload, error) { if err := r.authorize(ctx, input.TrustCenterID, probo.ActionTrustCenterAccessCreate); err != nil {