Introduce pkg/mail.Addr

Signed-off-by: Émile Ré <nemile.re@gmail.com>
This commit is contained in:
Émile Ré
2025-12-12 15:32:46 +01:00
parent cab9d6f2ed
commit c0b2a5702d
85 changed files with 842 additions and 646 deletions

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<31b0b8ac2c59ccf3addd4508d56bd166>> * @generated SignedSource<<b1826b0f39e35064b4f2c8bc9691f63a>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -12,7 +12,7 @@ import { ConcreteRequest } from 'relay-runtime';
export type MembershipRole = "ADMIN" | "AUDITOR" | "EMPLOYEE" | "OWNER" | "VIEWER"; export type MembershipRole = "ADMIN" | "AUDITOR" | "EMPLOYEE" | "OWNER" | "VIEWER";
export type InviteUserInput = { export type InviteUserInput = {
createPeople: boolean; createPeople: boolean;
email: string; email: any;
fullName: string; fullName: string;
organizationId: string; organizationId: string;
role: MembershipRole; role: MembershipRole;
@@ -27,7 +27,7 @@ export type InviteUserDialogMutation$data = {
readonly node: { readonly node: {
readonly acceptedAt: any | null | undefined; readonly acceptedAt: any | null | undefined;
readonly createdAt: any; readonly createdAt: any;
readonly email: string; readonly email: any;
readonly expiresAt: any; readonly expiresAt: any;
readonly fullName: string; readonly fullName: string;
readonly id: string; readonly id: string;

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<9b84adc0253186c6f0a5f47837d83d7f>> * @generated SignedSource<<f9018169cee4d71768fca395e5dd59a9>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -11,7 +11,7 @@
import { ConcreteRequest } from 'relay-runtime'; import { ConcreteRequest } from 'relay-runtime';
export type BulkExportDocumentsInput = { export type BulkExportDocumentsInput = {
documentIds: ReadonlyArray<string>; documentIds: ReadonlyArray<string>;
watermarkEmail?: string | null | undefined; watermarkEmail?: any | null | undefined;
withSignatures: boolean; withSignatures: boolean;
withWatermark: boolean; withWatermark: boolean;
}; };

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<e263259b92f7b1fe14bca5fca49b20bf>> * @generated SignedSource<<5175c19e84ee71ec4a3e9e35e515991e>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -15,14 +15,14 @@ export type PeopleGraphNodeQuery$variables = {
}; };
export type PeopleGraphNodeQuery$data = { export type PeopleGraphNodeQuery$data = {
readonly node: { readonly node: {
readonly additionalEmailAddresses?: ReadonlyArray<string>; readonly additionalEmailAddresses?: ReadonlyArray<any>;
readonly contractEndDate?: any | null | undefined; readonly contractEndDate?: any | null | undefined;
readonly contractStartDate?: any | null | undefined; readonly contractStartDate?: any | null | undefined;
readonly fullName?: string; readonly fullName?: string;
readonly id?: string; readonly id?: string;
readonly kind?: PeopleKind; readonly kind?: PeopleKind;
readonly position?: string | null | undefined; readonly position?: string | null | undefined;
readonly primaryEmailAddress?: string; readonly primaryEmailAddress?: any;
}; };
}; };
export type PeopleGraphNodeQuery = { export type PeopleGraphNodeQuery = {

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<940911e0b7ae1ffe53a31f98216dcfe6>> * @generated SignedSource<<336b3e8036d321c95981b2d15c0b41b9>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -17,14 +17,14 @@ export type PeopleGraphPaginatedFragment$data = {
readonly __id: string; readonly __id: string;
readonly edges: ReadonlyArray<{ readonly edges: ReadonlyArray<{
readonly node: { readonly node: {
readonly additionalEmailAddresses: ReadonlyArray<string>; readonly additionalEmailAddresses: ReadonlyArray<any>;
readonly contractEndDate: any | null | undefined; readonly contractEndDate: any | null | undefined;
readonly contractStartDate: any | null | undefined; readonly contractStartDate: any | null | undefined;
readonly fullName: string; readonly fullName: string;
readonly id: string; readonly id: string;
readonly kind: PeopleKind; readonly kind: PeopleKind;
readonly position: string | null | undefined; readonly position: string | null | undefined;
readonly primaryEmailAddress: string; readonly primaryEmailAddress: any;
}; };
}>; }>;
}; };

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<3b196ff5779051fcd1ed9cca6b7be112>> * @generated SignedSource<<8d8fcf4db72f5417db8a4f32129a687a>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -23,7 +23,7 @@ export type PeopleGraphQuery$data = {
readonly node: { readonly node: {
readonly fullName: string; readonly fullName: string;
readonly id: string; readonly id: string;
readonly primaryEmailAddress: string; readonly primaryEmailAddress: any;
}; };
}>; }>;
}; };

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<a91a49863ad5c692ae869d3bcc0584cb>> * @generated SignedSource<<904f3388abab4e5e84dddf0528530a46>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -11,14 +11,14 @@
import { ConcreteRequest } from 'relay-runtime'; import { ConcreteRequest } from 'relay-runtime';
export type PeopleKind = "CONTRACTOR" | "EMPLOYEE" | "SERVICE_ACCOUNT"; export type PeopleKind = "CONTRACTOR" | "EMPLOYEE" | "SERVICE_ACCOUNT";
export type UpdatePeopleInput = { export type UpdatePeopleInput = {
additionalEmailAddresses?: ReadonlyArray<string> | null | undefined; additionalEmailAddresses?: ReadonlyArray<any> | null | undefined;
contractEndDate?: any | null | undefined; contractEndDate?: any | null | undefined;
contractStartDate?: any | null | undefined; contractStartDate?: any | null | undefined;
fullName?: string | null | undefined; fullName?: string | null | undefined;
id: string; id: string;
kind?: PeopleKind | null | undefined; kind?: PeopleKind | null | undefined;
position?: string | null | undefined; position?: string | null | undefined;
primaryEmailAddress?: string | null | undefined; primaryEmailAddress?: any | null | undefined;
}; };
export type PeopleGraphUpdateMutation$variables = { export type PeopleGraphUpdateMutation$variables = {
input: UpdatePeopleInput; input: UpdatePeopleInput;
@@ -26,14 +26,14 @@ export type PeopleGraphUpdateMutation$variables = {
export type PeopleGraphUpdateMutation$data = { export type PeopleGraphUpdateMutation$data = {
readonly updatePeople: { readonly updatePeople: {
readonly people: { readonly people: {
readonly additionalEmailAddresses: ReadonlyArray<string>; readonly additionalEmailAddresses: ReadonlyArray<any>;
readonly contractEndDate: any | null | undefined; readonly contractEndDate: any | null | undefined;
readonly contractStartDate: any | null | undefined; readonly contractStartDate: any | null | undefined;
readonly fullName: string; readonly fullName: string;
readonly id: string; readonly id: string;
readonly kind: PeopleKind; readonly kind: PeopleKind;
readonly position: string | null | undefined; readonly position: string | null | undefined;
readonly primaryEmailAddress: string; readonly primaryEmailAddress: any;
}; };
}; };
}; };

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<271260153dc611541b234266fbdd0c74>> * @generated SignedSource<<213636cdb170859b30c372b29ddbf752>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -11,7 +11,7 @@
import { ConcreteRequest } from 'relay-runtime'; import { ConcreteRequest } from 'relay-runtime';
export type CreateTrustCenterAccessInput = { export type CreateTrustCenterAccessInput = {
active: boolean; active: boolean;
email: string; email: any;
name: string; name: string;
trustCenterId: string; trustCenterId: string;
}; };
@@ -27,7 +27,7 @@ export type TrustCenterAccessGraphCreateMutation$data = {
readonly active: boolean; readonly active: boolean;
readonly activeCount: number; readonly activeCount: number;
readonly createdAt: any; readonly createdAt: any;
readonly email: string; readonly email: any;
readonly hasAcceptedNonDisclosureAgreement: boolean; readonly hasAcceptedNonDisclosureAgreement: boolean;
readonly id: string; readonly id: string;
readonly lastTokenExpiresAt: any | null | undefined; readonly lastTokenExpiresAt: any | null | undefined;

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<4d6afeeeed479773202c8516eeb63620>> * @generated SignedSource<<3c70b46fb46bf10a48eb06a5e76c1d75>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -31,7 +31,7 @@ export type TrustCenterAccessGraphUpdateMutation$data = {
readonly active: boolean; readonly active: boolean;
readonly activeCount: number; readonly activeCount: number;
readonly createdAt: any; readonly createdAt: any;
readonly email: string; readonly email: any;
readonly hasAcceptedNonDisclosureAgreement: boolean; readonly hasAcceptedNonDisclosureAgreement: boolean;
readonly id: string; readonly id: string;
readonly lastTokenExpiresAt: any | null | undefined; readonly lastTokenExpiresAt: any | null | undefined;

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<a3da155281753945ab0645a04f0b74fb>> * @generated SignedSource<<5b0e12ba40232dd4328c0fd766513fb2>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -19,7 +19,7 @@ export type TrustCenterAccessGraph_accesses$data = {
readonly active: boolean; readonly active: boolean;
readonly activeCount: number; readonly activeCount: number;
readonly createdAt: any; readonly createdAt: any;
readonly email: string; readonly email: any;
readonly hasAcceptedNonDisclosureAgreement: boolean; readonly hasAcceptedNonDisclosureAgreement: boolean;
readonly id: string; readonly id: string;
readonly lastTokenExpiresAt: any | null | undefined; readonly lastTokenExpiresAt: any | null | undefined;

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<626f9dd0bec23ddd103042d53539363a>> * @generated SignedSource<<abb20ee18aca56681898a0390db03bfc>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -21,7 +21,7 @@ export type EmployeeLayoutQuery$data = {
readonly viewer: { readonly viewer: {
readonly id: string; readonly id: string;
readonly user: { readonly user: {
readonly email: string; readonly email: any;
readonly fullName: string; readonly fullName: string;
}; };
}; };

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<b7983d3d3c089aa0efebaab639de76fb>> * @generated SignedSource<<5e4ec3cfaeb52bc478d496e37349880c>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -21,7 +21,7 @@ export type MainLayoutQuery$data = {
readonly viewer: { readonly viewer: {
readonly id: string; readonly id: string;
readonly user: { readonly user: {
readonly email: string; readonly email: any;
readonly fullName: string; readonly fullName: string;
}; };
}; };

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<f860e097afe4111c629ecf78d873a84e>> * @generated SignedSource<<3a082179d9ea9d47824b7ee925418b95>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -11,7 +11,7 @@
import { ConcreteRequest } from 'relay-runtime'; import { ConcreteRequest } from 'relay-runtime';
export type ExportDocumentVersionPDFInput = { export type ExportDocumentVersionPDFInput = {
documentVersionId: string; documentVersionId: string;
watermarkEmail?: string | null | undefined; watermarkEmail?: any | null | undefined;
withSignatures: boolean; withSignatures: boolean;
withWatermark: boolean; withWatermark: boolean;
}; };

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<c1a6e41382db3951401d70c039c49f01>> * @generated SignedSource<<b5ef7f2a5ceb3688743c82362f625a55>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -13,7 +13,7 @@ export type DocumentDetailPageUserEmailQuery$variables = Record<PropertyKey, nev
export type DocumentDetailPageUserEmailQuery$data = { export type DocumentDetailPageUserEmailQuery$data = {
readonly viewer: { readonly viewer: {
readonly user: { readonly user: {
readonly email: string; readonly email: any;
}; };
}; };
}; };

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<7ff59bf602dca8eafe0a671cb7e95aad>> * @generated SignedSource<<959a59c16a4d27856820501f80379429>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -13,7 +13,7 @@ export type DocumentsPageUserEmailQuery$variables = Record<PropertyKey, never>;
export type DocumentsPageUserEmailQuery$data = { export type DocumentsPageUserEmailQuery$data = {
readonly viewer: { readonly viewer: {
readonly user: { readonly user: {
readonly email: string; readonly email: any;
}; };
}; };
}; };

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<e3ddbde4beb849447cb819f8991245a7>> * @generated SignedSource<<7d45f232043aa77c8e1e4ae7f8f330db>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -17,7 +17,7 @@ export type DocumentSignaturesTab_signature$data = {
readonly signedAt: any | null | undefined; readonly signedAt: any | null | undefined;
readonly signedBy: { readonly signedBy: {
readonly fullName: string; readonly fullName: string;
readonly primaryEmailAddress: string; readonly primaryEmailAddress: any;
}; };
readonly state: DocumentVersionSignatureState; readonly state: DocumentVersionSignatureState;
readonly " $fragmentType": "DocumentSignaturesTab_signature"; readonly " $fragmentType": "DocumentSignaturesTab_signature";

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<328e9d55b6661d3330cb961426393d6f>> * @generated SignedSource<<807a8f8c5c320dadd6b9672ef48583f2>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -22,7 +22,7 @@ export type DocumentSignaturesTab_version$data = {
readonly signedBy: { readonly signedBy: {
readonly fullName: string; readonly fullName: string;
readonly id: string; readonly id: string;
readonly primaryEmailAddress: string; readonly primaryEmailAddress: any;
}; };
readonly state: DocumentVersionSignatureState; readonly state: DocumentVersionSignatureState;
readonly " $fragmentSpreads": FragmentRefs<"DocumentSignaturesTab_signature">; readonly " $fragmentSpreads": FragmentRefs<"DocumentSignaturesTab_signature">;

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<cf446d4f8991b5ff332b3d81174c3247>> * @generated SignedSource<<22054346a606e1ce4a26b02218f87af9>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -11,14 +11,14 @@
import { ConcreteRequest } from 'relay-runtime'; import { ConcreteRequest } from 'relay-runtime';
export type PeopleKind = "CONTRACTOR" | "EMPLOYEE" | "SERVICE_ACCOUNT"; export type PeopleKind = "CONTRACTOR" | "EMPLOYEE" | "SERVICE_ACCOUNT";
export type CreatePeopleInput = { export type CreatePeopleInput = {
additionalEmailAddresses?: ReadonlyArray<string> | null | undefined; additionalEmailAddresses?: ReadonlyArray<any> | null | undefined;
contractEndDate?: any | null | undefined; contractEndDate?: any | null | undefined;
contractStartDate?: any | null | undefined; contractStartDate?: any | null | undefined;
fullName: string; fullName: string;
kind: PeopleKind; kind: PeopleKind;
organizationId: string; organizationId: string;
position?: string | null | undefined; position?: string | null | undefined;
primaryEmailAddress: string; primaryEmailAddress: any;
}; };
export type CreatePeopleDialogMutation$variables = { export type CreatePeopleDialogMutation$variables = {
connections: ReadonlyArray<string>; connections: ReadonlyArray<string>;
@@ -28,12 +28,12 @@ export type CreatePeopleDialogMutation$data = {
readonly createPeople: { readonly createPeople: {
readonly peopleEdge: { readonly peopleEdge: {
readonly node: { readonly node: {
readonly additionalEmailAddresses: ReadonlyArray<string>; readonly additionalEmailAddresses: ReadonlyArray<any>;
readonly fullName: string; readonly fullName: string;
readonly id: string; readonly id: string;
readonly kind: PeopleKind; readonly kind: PeopleKind;
readonly position: string | null | undefined; readonly position: string | null | undefined;
readonly primaryEmailAddress: string; readonly primaryEmailAddress: any;
}; };
}; };
}; };

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<dd7d5d34935f87fcfb658ca6cfc5e794>> * @generated SignedSource<<e3763975ef1132c3e2f909019b6cd98c>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -20,7 +20,7 @@ export type MembersSettingsTabInvitationsFragment$data = {
readonly node: { readonly node: {
readonly acceptedAt: any | null | undefined; readonly acceptedAt: any | null | undefined;
readonly createdAt: any; readonly createdAt: any;
readonly email: string; readonly email: any;
readonly expiresAt: any; readonly expiresAt: any;
readonly fullName: string; readonly fullName: string;
readonly id: string; readonly id: string;

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<c3bf3676d3f8688315fc342efcf54547>> * @generated SignedSource<<21ae595420b0098920e8d7f28834c7af>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -20,7 +20,7 @@ export type MembersSettingsTabMembershipsFragment$data = {
readonly node: { readonly node: {
readonly authMethod: UserAuthMethod; readonly authMethod: UserAuthMethod;
readonly createdAt: any; readonly createdAt: any;
readonly emailAddress: string; readonly emailAddress: any;
readonly fullName: string; readonly fullName: string;
readonly id: string; readonly id: string;
readonly role: MembershipRole; readonly role: MembershipRole;

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<843a80b3503ff276081e69b558dcce81>> * @generated SignedSource<<ac1551146f55ada33ee647ca3985d467>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -11,7 +11,7 @@
import { ConcreteRequest } from 'relay-runtime'; import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime"; import { FragmentRefs } from "relay-runtime";
export type CreateVendorContactInput = { export type CreateVendorContactInput = {
email?: string | null | undefined; email?: any | null | undefined;
fullName?: string | null | undefined; fullName?: string | null | undefined;
phone?: string | null | undefined; phone?: string | null | undefined;
role?: string | null | undefined; role?: string | null | undefined;

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<d22f20fafedcdddc50cd21917a8a5c28>> * @generated SignedSource<<00c307339357baa16c237c563b8f32bd>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -11,7 +11,7 @@
import { ConcreteRequest } from 'relay-runtime'; import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime"; import { FragmentRefs } from "relay-runtime";
export type UpdateVendorContactInput = { export type UpdateVendorContactInput = {
email?: string | null | undefined; email?: any | null | undefined;
fullName?: string | null | undefined; fullName?: string | null | undefined;
id: string; id: string;
phone?: string | null | undefined; phone?: string | null | undefined;

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<28eef433ba6749a95468492e8ac9d1a6>> * @generated SignedSource<<8020981cbf1e1b2b59c3ae9027973b17>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -12,7 +12,7 @@ import { ReaderFragment } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime"; import { FragmentRefs } from "relay-runtime";
export type VendorContactsTabFragment_contact$data = { export type VendorContactsTabFragment_contact$data = {
readonly createdAt: any; readonly createdAt: any;
readonly email: string | null | undefined; readonly email: any | null | undefined;
readonly fullName: string | null | undefined; readonly fullName: string | null | undefined;
readonly id: string; readonly id: string;
readonly phone: string | null | undefined; readonly phone: string | null | undefined;

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<446953c5eafa3b9a465d245da3bea1af>> * @generated SignedSource<<5344ac5d6a506485d45e399361b92299>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -11,7 +11,7 @@
import { ConcreteRequest } from 'relay-runtime'; import { ConcreteRequest } from 'relay-runtime';
export type RequestDocumentAccessInput = { export type RequestDocumentAccessInput = {
documentId: string; documentId: string;
email?: string | null | undefined; email?: any | null | undefined;
name?: string | null | undefined; name?: string | null | undefined;
trustCenterId: string; trustCenterId: string;
}; };

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<898167474e2680273ad8b588a63a47c5>> * @generated SignedSource<<19ada29ea9b8f557b4fcc39f57bc3aec>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -10,7 +10,7 @@
import { ConcreteRequest } from 'relay-runtime'; import { ConcreteRequest } from 'relay-runtime';
export type RequestAllAccessesInput = { export type RequestAllAccessesInput = {
email?: string | null | undefined; email?: any | null | undefined;
name?: string | null | undefined; name?: string | null | undefined;
trustCenterId: string; trustCenterId: string;
}; };

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<74bc40a8998c962930b3f0b63dc6acd9>> * @generated SignedSource<<858effd78b88a123430edcf6fcb100ed>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -10,7 +10,7 @@
import { ConcreteRequest } from 'relay-runtime'; import { ConcreteRequest } from 'relay-runtime';
export type RequestReportAccessInput = { export type RequestReportAccessInput = {
email?: string | null | undefined; email?: any | null | undefined;
name?: string | null | undefined; name?: string | null | undefined;
reportId: string; reportId: string;
trustCenterId: string; trustCenterId: string;

View File

@@ -1,5 +1,5 @@
/** /**
* @generated SignedSource<<4c07d5c8e345a8e3015f512403927890>> * @generated SignedSource<<32242c5e0b4dc9c9368c0e2f4034d95e>>
* @lightSyntaxTransform * @lightSyntaxTransform
* @nogrep * @nogrep
*/ */
@@ -10,7 +10,7 @@
import { ConcreteRequest } from 'relay-runtime'; import { ConcreteRequest } from 'relay-runtime';
export type RequestTrustCenterFileAccessInput = { export type RequestTrustCenterFileAccessInput = {
email?: string | null | undefined; email?: any | null | undefined;
name?: string | null | undefined; name?: string | null | undefined;
trustCenterFileId: string; trustCenterFileId: string;
trustCenterId: string; trustCenterId: string;

View File

@@ -20,6 +20,7 @@ import (
"github.com/crewjam/saml" "github.com/crewjam/saml"
"go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/mail"
) )
func ExtractAttributeValue(assertion *saml.Assertion, attributeName string) (string, error) { func ExtractAttributeValue(assertion *saml.Assertion, attributeName string) (string, error) {
@@ -98,25 +99,32 @@ func isValidRole(role string) bool {
func ExtractUserAttributes( func ExtractUserAttributes(
assertion *saml.Assertion, assertion *saml.Assertion,
attributeEmail, attributeFirstname, attributeLastname, attributeRole string, attributeEmail, attributeFirstname, attributeLastname, attributeRole string,
) (email, fullname, role string, err error) { ) (email mail.Addr, fullname, role string, err error) {
if len(assertion.AttributeStatements) == 0 { if len(assertion.AttributeStatements) == 0 {
if assertion.Subject != nil && assertion.Subject.NameID != nil { if assertion.Subject != nil && assertion.Subject.NameID != nil {
email = assertion.Subject.NameID.Value email, err = mail.ParseAddr(assertion.Subject.NameID.Value)
fullname = email if err != nil {
return mail.Nil, "", "", fmt.Errorf("invalid nameID as email address")
}
fullname = email.String()
role = "" role = ""
return email, fullname, role, nil return email, fullname, role, nil
} }
return "", "", "", fmt.Errorf("no attribute statement and no NameID in assertion") return mail.Nil, "", "", fmt.Errorf("no attribute statement and no NameID in assertion")
} }
email, err = ExtractAttributeValue(assertion, attributeEmail) emailString, err := ExtractAttributeValue(assertion, attributeEmail)
if err != nil { if err != nil {
if assertion.Subject != nil && assertion.Subject.NameID != nil { if assertion.Subject != nil && assertion.Subject.NameID != nil {
email = assertion.Subject.NameID.Value emailString = assertion.Subject.NameID.Value
} else { } else {
return "", "", "", fmt.Errorf("cannot extract email: %w", err) return mail.Nil, "", "", fmt.Errorf("cannot extract email: %w", err)
} }
} }
email, err = mail.ParseAddr(emailString)
if err != nil {
return mail.Nil, "", "", fmt.Errorf("invalid attribute email")
}
firstname, err := ExtractAttributeValue(assertion, attributeFirstname) firstname, err := ExtractAttributeValue(assertion, attributeFirstname)
if err != nil { if err != nil {
@@ -135,7 +143,7 @@ func ExtractUserAttributes(
} else if lastname != "" { } else if lastname != "" {
fullname = lastname fullname = lastname
} else { } else {
fullname = email fullname = email.String()
} }
role, err = ExtractAttributeValue(assertion, attributeRole) role, err = ExtractAttributeValue(assertion, attributeRole)

View File

@@ -32,6 +32,7 @@ import (
"go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/crypto/cipher" "go.probo.inc/probo/pkg/crypto/cipher"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
) )
type ( type (
@@ -372,7 +373,7 @@ func (s *SAMLService) InitiateSAMLLogin(
} }
type SAMLUserInfo struct { type SAMLUserInfo struct {
Email string Email mail.Addr
FullName string FullName string
Role *coredata.MembershipRole Role *coredata.MembershipRole
SAMLSubject string SAMLSubject string
@@ -516,13 +517,8 @@ func (s *SAMLService) HandleSAMLAssertion(
return nil, ErrCannotExtractUserAttributes{Err: err} return nil, ErrCannotExtractUserAttributes{Err: err}
} }
actualEmailDomain, err := ExtractEmailDomain(email) if email.Domain() != config.EmailDomain {
if err != nil { return nil, fmt.Errorf("email domain mismatch: assertion contains email with domain %s but SAML config is for domain %s", email.Domain(), config.EmailDomain)
return nil, ErrCannotExtractUserAttributes{Err: fmt.Errorf("cannot extract domain from email: %w", err)}
}
if actualEmailDomain != config.EmailDomain {
return nil, fmt.Errorf("email domain mismatch: assertion contains email with domain %s but SAML config is for domain %s", actualEmailDomain, config.EmailDomain)
} }
systemRole := MapSAMLRoleToSystemRole(samlRole) systemRole := MapSAMLRoleToSystemRole(samlRole)

View File

@@ -21,7 +21,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"net" "net"
"net/mail"
"time" "time"
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
@@ -32,6 +31,7 @@ import (
"go.probo.inc/probo/pkg/crypto/cipher" "go.probo.inc/probo/pkg/crypto/cipher"
"go.probo.inc/probo/pkg/crypto/passwdhash" "go.probo.inc/probo/pkg/crypto/passwdhash"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/statelesstoken" "go.probo.inc/probo/pkg/statelesstoken"
) )
@@ -102,8 +102,8 @@ type (
ErrSAMLAutoSignupDisabled struct{} ErrSAMLAutoSignupDisabled struct{}
EmailConfirmationData struct { EmailConfirmationData struct {
UserID gid.GID `json:"uid"` UserID gid.GID `json:"uid"`
Email string `json:"email"` Email mail.Addr `json:"email"`
} }
InvitationData struct { InvitationData struct {
@@ -113,7 +113,7 @@ type (
CreatePeople bool `json:"create_people"` CreatePeople bool `json:"create_people"`
} }
PasswordResetData struct { PasswordResetData struct {
Email string `json:"email"` Email mail.Addr `json:"email"`
} }
ErrSAMLAuthRequired struct { ErrSAMLAuthRequired struct {
@@ -262,7 +262,7 @@ func (s Service) CanCreateOrganization(ctx context.Context, userID gid.GID) erro
func (s Service) ForgetPassword( func (s Service) ForgetPassword(
ctx context.Context, ctx context.Context,
email string, email mail.Addr,
) error { ) error {
// Always generate a new token to avoid timing attacks and leaking information // Always generate a new token to avoid timing attacks and leaking information
// about existing emails // about existing emails
@@ -329,7 +329,7 @@ func (s Service) ForgetPassword(
func (s Service) SignUp( func (s Service) SignUp(
ctx context.Context, ctx context.Context,
emailAddress string, emailAddress mail.Addr,
password string, password string,
fullName string, fullName string,
) (*coredata.User, *coredata.Session, error) { ) (*coredata.User, *coredata.Session, error) {
@@ -337,11 +337,6 @@ func (s Service) SignUp(
return nil, nil, &ErrSignupDisabled{} return nil, nil, &ErrSignupDisabled{}
} }
emailAddress2, err := mail.ParseAddress(emailAddress)
if err != nil {
return nil, nil, &ErrInvalidEmail{emailAddress}
}
if len(password) < 8 || len(password) > 128 { if len(password) < 8 || len(password) > 128 {
return nil, nil, &ErrInvalidPassword{minLength: 8, maxLength: 128} return nil, nil, &ErrInvalidPassword{minLength: 8, maxLength: 128}
} }
@@ -358,7 +353,7 @@ func (s Service) SignUp(
now := time.Now() now := time.Now()
user := &coredata.User{ user := &coredata.User{
ID: gid.New(gid.NilTenant, coredata.UserEntityType), ID: gid.New(gid.NilTenant, coredata.UserEntityType),
EmailAddress: emailAddress2.Address, EmailAddress: emailAddress,
HashedPassword: hashedPassword, HashedPassword: hashedPassword,
EmailAddressVerified: false, EmailAddressVerified: false,
FullName: fullName, FullName: fullName,
@@ -453,16 +448,12 @@ func (s Service) ProvisionSAMLUser(
ctx context.Context, ctx context.Context,
samlConfigID gid.GID, samlConfigID gid.GID,
organizationID gid.GID, organizationID gid.GID,
emailAddress string, emailAddress mail.Addr,
fullName string, fullName string,
samlSubject string, samlSubject string,
existingSession *coredata.Session, existingSession *coredata.Session,
sessionDuration time.Duration, sessionDuration time.Duration,
) (*coredata.Session, *coredata.User, error) { ) (*coredata.Session, *coredata.User, error) {
if _, err := mail.ParseAddress(emailAddress); err != nil {
return nil, nil, &ErrInvalidEmail{emailAddress}
}
if fullName == "" { if fullName == "" {
return nil, nil, &ErrInvalidFullName{fullName} return nil, nil, &ErrInvalidFullName{fullName}
} }
@@ -566,14 +557,10 @@ func (s Service) ProvisionSAMLUser(
func (s Service) SignIn( func (s Service) SignIn(
ctx context.Context, ctx context.Context,
emailAddress string, emailAddress mail.Addr,
password string, password string,
existingSession *coredata.Session, existingSession *coredata.Session,
) (*coredata.Session, *coredata.User, error) { ) (*coredata.Session, *coredata.User, error) {
if _, err := mail.ParseAddress(emailAddress); err != nil {
return nil, nil, &ErrInvalidCredentials{"invalid email or password"}
}
user := &coredata.User{} user := &coredata.User{}
session := &coredata.Session{} session := &coredata.Session{}
@@ -868,10 +855,6 @@ func (s Service) SignupFromInvitation(
return nil, nil, &ErrInvalidPassword{minLength: 8, maxLength: 128} return nil, nil, &ErrInvalidPassword{minLength: 8, maxLength: 128}
} }
if _, err := mail.ParseAddress(payload.Data.Email); err != nil {
return nil, nil, &ErrInvalidEmail{payload.Data.Email}
}
if fullName == "" { if fullName == "" {
fullName = payload.Data.FullName fullName = payload.Data.FullName
} }
@@ -980,7 +963,7 @@ func (s *TenantAuthService) GetUserAuthMethod(
} }
// User has SAML subject - check if there's SAML config for this org + user's domain // User has SAML subject - check if there's SAML config for this org + user's domain
domain := extractDomain(user.EmailAddress) domain := user.EmailAddress.Domain()
if domain == "" { if domain == "" {
authMethod = coredata.UserAuthMethodPassword authMethod = coredata.UserAuthMethodPassword
return nil return nil
@@ -1005,20 +988,6 @@ func (s *TenantAuthService) GetUserAuthMethod(
return authMethod, err return authMethod, err
} }
func extractDomain(email string) string {
atIndex := -1
for i := 0; i < len(email); i++ {
if email[i] == '@' {
atIndex = i
break
}
}
if atIndex == -1 || atIndex == len(email)-1 {
return ""
}
return email[atIndex+1:]
}
// CheckOrganizationAccess checks access to multiple organizations in a single database query // CheckOrganizationAccess checks access to multiple organizations in a single database query
// Always uses batch processing to avoid N+1 query problems // Always uses batch processing to avoid N+1 query problems
func (s Service) CheckOrganizationAccess( func (s Service) CheckOrganizationAccess(
@@ -1029,7 +998,7 @@ func (s Service) CheckOrganizationAccess(
) (map[gid.GID]AccessResult, error) { ) (map[gid.GID]AccessResult, error) {
results := make(map[gid.GID]AccessResult, len(organizationIDs)) results := make(map[gid.GID]AccessResult, len(organizationIDs))
domain := extractDomain(user.EmailAddress) domain := user.EmailAddress.Domain()
if domain == "" { if domain == "" {
// All organizations fail with invalid email // All organizations fail with invalid email
for _, orgID := range organizationIDs { for _, orgID := range organizationIDs {

View File

@@ -26,6 +26,7 @@ import (
"go.probo.inc/probo/packages/emails" "go.probo.inc/probo/packages/emails"
"go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/page"
"go.probo.inc/probo/pkg/statelesstoken" "go.probo.inc/probo/pkg/statelesstoken"
) )
@@ -242,7 +243,7 @@ func (s *Service) AcceptInvitationByID(
type UserInvitation struct { type UserInvitation struct {
ID gid.GID ID gid.GID
Email string Email mail.Addr
FullName string FullName string
Role coredata.MembershipRole Role coredata.MembershipRole
ExpiresAt time.Time ExpiresAt time.Time
@@ -259,7 +260,7 @@ type OrganizationSummary struct {
func (s *Service) GetUserPendingInvitations( func (s *Service) GetUserPendingInvitations(
ctx context.Context, ctx context.Context,
email string, email mail.Addr,
) ([]*UserInvitation, error) { ) ([]*UserInvitation, error) {
userInvitations := []*UserInvitation{} userInvitations := []*UserInvitation{}
@@ -679,7 +680,7 @@ func (s *TenantAuthzService) UpdateMembershipRole(
func (s *TenantAuthzService) InviteUserToOrganization( func (s *TenantAuthzService) InviteUserToOrganization(
ctx context.Context, ctx context.Context,
organizationID gid.GID, organizationID gid.GID,
emailAddress string, emailAddress mail.Addr,
fullName string, fullName string,
role coredata.MembershipRole, role coredata.MembershipRole,
) (*coredata.Invitation, error) { ) (*coredata.Invitation, error) {

View File

@@ -24,6 +24,7 @@ import (
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg" "go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/page"
) )
@@ -701,7 +702,7 @@ func (p *Document) IsLastSignableVersionSignedByUserEmail(
conn pg.Conn, conn pg.Conn,
scope Scoper, scope Scoper,
documentID gid.GID, documentID gid.GID,
userEmail string, userEmail mail.Addr,
) (bool, error) { ) (bool, error) {
q := ` q := `
WITH last_signable_version AS ( WITH last_signable_version AS (

View File

@@ -16,6 +16,7 @@ package coredata
import ( import (
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
"go.probo.inc/probo/pkg/mail"
) )
type ( type (
@@ -23,7 +24,7 @@ type (
query *string query *string
trustCenterVisibilities []TrustCenterVisibility trustCenterVisibilities []TrustCenterVisibility
published *bool published *bool
userEmail *string userEmail *mail.Addr
} }
) )
@@ -47,7 +48,7 @@ func (f *DocumentFilter) WithPublished(published *bool) *DocumentFilter {
return f return f
} }
func (f *DocumentFilter) WithUserEmail(userEmail *string) *DocumentFilter { func (f *DocumentFilter) WithUserEmail(userEmail *mail.Addr) *DocumentFilter {
f.userEmail = userEmail f.userEmail = userEmail
return f return f
} }

View File

@@ -16,11 +16,12 @@ package coredata
import ( import (
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
"go.probo.inc/probo/pkg/mail"
) )
type ( type (
DocumentVersionFilter struct { DocumentVersionFilter struct {
userEmail *string userEmail *mail.Addr
} }
) )
@@ -28,7 +29,7 @@ func NewDocumentVersionFilter() *DocumentVersionFilter {
return &DocumentVersionFilter{} return &DocumentVersionFilter{}
} }
func (f *DocumentVersionFilter) WithUserEmail(userEmail *string) *DocumentVersionFilter { func (f *DocumentVersionFilter) WithUserEmail(userEmail *mail.Addr) *DocumentVersionFilter {
f.userEmail = userEmail f.userEmail = userEmail
return f return f
} }

View File

@@ -25,6 +25,7 @@ import (
"github.com/jackc/pgx/v5/pgconn" "github.com/jackc/pgx/v5/pgconn"
"go.gearno.de/kit/pg" "go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/page"
) )
@@ -424,7 +425,7 @@ func (pvs *DocumentVersionSignature) IsSignedByUserEmail(
conn pg.Conn, conn pg.Conn,
scope Scoper, scope Scoper,
documentVersionID gid.GID, documentVersionID gid.GID,
userEmail string, userEmail mail.Addr,
) (bool, error) { ) (bool, error) {
q := ` q := `
SELECT EXISTS ( SELECT EXISTS (

View File

@@ -23,6 +23,7 @@ import (
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg" "go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
) )
type ( type (
@@ -45,7 +46,7 @@ var (
func NewEmail( func NewEmail(
recipientName string, recipientName string,
recipientEmail string, recipientEmail mail.Addr,
subject string, subject string,
textBody string, textBody string,
htmlBody *string, htmlBody *string,
@@ -54,7 +55,7 @@ func NewEmail(
return &Email{ return &Email{
ID: gid.New(gid.NilTenant, EmailEntityType), ID: gid.New(gid.NilTenant, EmailEntityType),
RecipientName: recipientName, RecipientName: recipientName,
RecipientEmail: recipientEmail, RecipientEmail: recipientEmail.String(),
Subject: subject, Subject: subject,
TextBody: textBody, TextBody: textBody,
HtmlBody: htmlBody, HtmlBody: htmlBody,

View File

@@ -11,31 +11,32 @@ import (
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg" "go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
) )
type ( type (
ExportJob struct { ExportJob struct {
ID gid.GID `db:"id"` ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"` OrganizationID gid.GID `db:"organization_id"`
Type ExportJobType `db:"type"` Type ExportJobType `db:"type"`
Arguments json.RawMessage `db:"arguments"` Arguments json.RawMessage `db:"arguments"`
Error *string `db:"error"` Error *string `db:"error"`
Status ExportJobStatus `db:"status"` Status ExportJobStatus `db:"status"`
FileID *gid.GID `db:"file_id"` FileID *gid.GID `db:"file_id"`
RecipientEmail string `db:"recipient_email"` RecipientEmail mail.Addr `db:"recipient_email"`
RecipientName string `db:"recipient_name"` RecipientName string `db:"recipient_name"`
CreatedAt time.Time `db:"created_at"` CreatedAt time.Time `db:"created_at"`
StartedAt *time.Time `db:"started_at"` StartedAt *time.Time `db:"started_at"`
CompletedAt *time.Time `db:"completed_at"` CompletedAt *time.Time `db:"completed_at"`
} }
ExportJobs []*ExportJob ExportJobs []*ExportJob
DocumentExportArguments struct { DocumentExportArguments struct {
DocumentIDs []gid.GID `json:"document_ids"` DocumentIDs []gid.GID `json:"document_ids"`
WithWatermark bool `json:"with_watermark"` WithWatermark bool `json:"with_watermark"`
WatermarkEmail *string `json:"watermark_email"` WatermarkEmail *mail.Addr `json:"watermark_email"`
WithSignatures bool `json:"with_signatures"` WithSignatures bool `json:"with_signatures"`
} }
FrameworkExportArguments struct { FrameworkExportArguments struct {

View File

@@ -24,6 +24,7 @@ import (
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg" "go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/page"
) )
@@ -31,9 +32,9 @@ type (
Invitation struct { Invitation struct {
ID gid.GID `db:"id"` ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"` OrganizationID gid.GID `db:"organization_id"`
Email string `db:"email"` Email mail.Addr `db:"email"`
FullName string `db:"full_name"` FullName string `db:"full_name"`
Role MembershipRole `db:"role"` Role MembershipRole `db:"role"`
Status InvitationStatus `db:"status"` Status InvitationStatus `db:"status"`
ExpiresAt time.Time `db:"expires_at"` ExpiresAt time.Time `db:"expires_at"`
AcceptedAt *time.Time `db:"accepted_at"` AcceptedAt *time.Time `db:"accepted_at"`
@@ -43,10 +44,10 @@ type (
Invitations []*Invitation Invitations []*Invitation
InvitationData struct { InvitationData struct {
InvitationID gid.GID `json:"invitation_id"` InvitationID gid.GID `json:"invitation_id"`
OrganizationID gid.GID `json:"organization_id"` OrganizationID gid.GID `json:"organization_id"`
Email string `json:"email"` Email mail.Addr `json:"email"`
FullName string `json:"full_name"` FullName string `json:"full_name"`
Role MembershipRole `json:"role"` Role MembershipRole `json:"role"`
} }
@@ -241,7 +242,7 @@ func (i *Invitations) LoadByEmail(
ctx context.Context, ctx context.Context,
conn pg.Conn, conn pg.Conn,
scope Scoper, scope Scoper,
email string, email mail.Addr,
cursor *page.Cursor[InvitationOrderField], cursor *page.Cursor[InvitationOrderField],
filter *InvitationFilter, filter *InvitationFilter,
) error { ) error {

View File

@@ -26,6 +26,7 @@ import (
"github.com/jackc/pgx/v5/pgconn" "github.com/jackc/pgx/v5/pgconn"
"go.gearno.de/kit/pg" "go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/page"
) )
@@ -36,7 +37,7 @@ type (
OrganizationID gid.GID `db:"organization_id"` OrganizationID gid.GID `db:"organization_id"`
Role MembershipRole `db:"role"` Role MembershipRole `db:"role"`
FullName string `db:"full_name"` FullName string `db:"full_name"`
EmailAddress string `db:"email_address"` EmailAddress mail.Addr `db:"email_address"`
CreatedAt time.Time `db:"created_at"` CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"` UpdatedAt time.Time `db:"updated_at"`
} }

View File

@@ -24,22 +24,23 @@ import (
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg" "go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/page"
) )
type ( type (
People struct { People struct {
ID gid.GID `db:"id"` ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"` OrganizationID gid.GID `db:"organization_id"`
Kind PeopleKind `db:"kind"` Kind PeopleKind `db:"kind"`
FullName string `db:"full_name"` FullName string `db:"full_name"`
PrimaryEmailAddress string `db:"primary_email_address"` PrimaryEmailAddress mail.Addr `db:"primary_email_address"`
AdditionalEmailAddresses []string `db:"additional_email_addresses"` AdditionalEmailAddresses []mail.Addr `db:"additional_email_addresses"`
Position *string `db:"position"` Position *string `db:"position"`
ContractStartDate *time.Time `db:"contract_start_date"` ContractStartDate *time.Time `db:"contract_start_date"`
ContractEndDate *time.Time `db:"contract_end_date"` ContractEndDate *time.Time `db:"contract_end_date"`
CreatedAt time.Time `db:"created_at"` CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"` UpdatedAt time.Time `db:"updated_at"`
} }
Peoples []*People Peoples []*People
@@ -180,7 +181,7 @@ func (p *People) LoadByEmailAndOrganizationID(
ctx context.Context, ctx context.Context,
conn pg.Conn, conn pg.Conn,
scope Scoper, scope Scoper,
primaryEmailAddress string, primaryEmailAddress mail.Addr,
organizationID gid.GID, organizationID gid.GID,
) error { ) error {
q := ` q := `
@@ -221,7 +222,7 @@ LIMIT 1;
people, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[People]) people, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[People])
if err != nil { if err != nil {
if errors.Is(err, pgx.ErrNoRows) { if errors.Is(err, pgx.ErrNoRows) {
return &ErrPeopleNotFound{Identifier: primaryEmailAddress} return &ErrPeopleNotFound{Identifier: primaryEmailAddress.String()}
} }
return fmt.Errorf("cannot collect people: %w", err) return fmt.Errorf("cannot collect people: %w", err)

View File

@@ -20,9 +20,9 @@ import (
"maps" "maps"
"time" "time"
"go.probo.inc/probo/pkg/gid"
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg" "go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
) )
type SAMLConfiguration struct { type SAMLConfiguration struct {

View File

@@ -24,23 +24,24 @@ import (
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg" "go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
) )
type ( type (
SlackMessage struct { SlackMessage struct {
ID gid.GID `db:"id"` ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"` OrganizationID gid.GID `db:"organization_id"`
Type SlackMessageType `db:"type"` Type SlackMessageType `db:"type"`
Body map[string]any `db:"body"` Body map[string]any `db:"body"`
MessageTS *string `db:"message_ts"` MessageTS *string `db:"message_ts"`
ChannelID *string `db:"channel_id"` ChannelID *string `db:"channel_id"`
RequesterEmail *string `db:"requester_email"` RequesterEmail *mail.Addr `db:"requester_email"`
Metadata map[string]any `db:"metadata"` Metadata map[string]any `db:"metadata"`
InitialSlackMessageID gid.GID `db:"initial_slack_message_id"` InitialSlackMessageID gid.GID `db:"initial_slack_message_id"`
CreatedAt time.Time `db:"created_at"` CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"` UpdatedAt time.Time `db:"updated_at"`
SentAt *time.Time `db:"sent_at"` SentAt *time.Time `db:"sent_at"`
Error *string `db:"error"` Error *string `db:"error"`
} }
ErrNoUnsentSlackMessage struct{} ErrNoUnsentSlackMessage struct{}
@@ -61,7 +62,6 @@ func NewSlackMessage(
organizationID gid.GID, organizationID gid.GID,
messageType SlackMessageType, messageType SlackMessageType,
body map[string]any, body map[string]any,
requesterEmail *string,
) *SlackMessage { ) *SlackMessage {
now := time.Now() now := time.Now()
id := gid.New(scope.GetTenantID(), SlackMessageEntityType) id := gid.New(scope.GetTenantID(), SlackMessageEntityType)
@@ -70,7 +70,6 @@ func NewSlackMessage(
OrganizationID: organizationID, OrganizationID: organizationID,
Type: messageType, Type: messageType,
Body: body, Body: body,
RequesterEmail: requesterEmail,
InitialSlackMessageID: id, InitialSlackMessageID: id,
CreatedAt: now, CreatedAt: now,
UpdatedAt: now, UpdatedAt: now,
@@ -438,7 +437,7 @@ func (s *SlackMessage) LoadLatestByRequesterEmailAndType(
conn pg.Conn, conn pg.Conn,
scope Scoper, scope Scoper,
organizationID gid.GID, organizationID gid.GID,
requesterEmail string, requesterEmail mail.Addr,
messageType SlackMessageType, messageType SlackMessageType,
since time.Time, since time.Time,
) error { ) error {

View File

@@ -26,24 +26,25 @@ import (
"github.com/jackc/pgx/v5/pgconn" "github.com/jackc/pgx/v5/pgconn"
"go.gearno.de/kit/pg" "go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/page"
) )
type ( type (
TrustCenterAccess struct { TrustCenterAccess struct {
ID gid.GID `db:"id"` ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"` OrganizationID gid.GID `db:"organization_id"`
TenantID gid.TenantID `db:"tenant_id"` TenantID gid.TenantID `db:"tenant_id"`
TrustCenterID gid.GID `db:"trust_center_id"` TrustCenterID gid.GID `db:"trust_center_id"`
Email string `db:"email"` Email mail.Addr `db:"email"`
Name string `db:"name"` Name string `db:"name"`
Active bool `db:"active"` Active bool `db:"active"`
HasAcceptedNonDisclosureAgreement bool `db:"has_accepted_non_disclosure_agreement"` HasAcceptedNonDisclosureAgreement bool `db:"has_accepted_non_disclosure_agreement"`
HasAcceptedNonDisclosureAgreementMetadata json.RawMessage `db:"has_accepted_non_disclosure_agreement_metadata"` HasAcceptedNonDisclosureAgreementMetadata json.RawMessage `db:"has_accepted_non_disclosure_agreement_metadata"`
NDAFileID *gid.GID `db:"nda_file_id"` NDAFileID *gid.GID `db:"nda_file_id"`
CreatedAt time.Time `db:"created_at"` CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"` UpdatedAt time.Time `db:"updated_at"`
LastTokenExpiresAt *time.Time `db:"last_token_expires_at"` LastTokenExpiresAt *time.Time `db:"last_token_expires_at"`
} }
TrustCenterAccesses []*TrustCenterAccess TrustCenterAccesses []*TrustCenterAccess
@@ -132,7 +133,7 @@ func (tca *TrustCenterAccess) LoadByTrustCenterIDAndEmail(
conn pg.Conn, conn pg.Conn,
scope Scoper, scope Scoper,
trustCenterID gid.GID, trustCenterID gid.GID,
email string, email mail.Addr,
) error { ) error {
q := ` q := `
SELECT SELECT

View File

@@ -22,23 +22,24 @@ import (
"strings" "strings"
"time" "time"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/page"
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn" "github.com/jackc/pgx/v5/pgconn"
"go.gearno.de/kit/pg" "go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page"
) )
type ( type (
User struct { User struct {
ID gid.GID `db:"id"` ID gid.GID `db:"id"`
EmailAddress string `db:"email_address"` EmailAddress mail.Addr `db:"email_address"`
HashedPassword []byte `db:"hashed_password"` HashedPassword []byte `db:"hashed_password"`
FullName string `db:"fullname"` FullName string `db:"fullname"`
EmailAddressVerified bool `db:"email_address_verified"` EmailAddressVerified bool `db:"email_address_verified"`
SAMLSubject *string `db:"saml_subject"` SAMLSubject *string `db:"saml_subject"`
CreatedAt time.Time `db:"created_at"` CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"` UpdatedAt time.Time `db:"updated_at"`
} }
Users []*User Users []*User
@@ -150,7 +151,7 @@ WHERE
func (u *User) LoadByEmail( func (u *User) LoadByEmail(
ctx context.Context, ctx context.Context,
conn pg.Conn, conn pg.Conn,
email string, email mail.Addr,
) error { ) error {
q := ` q := `
SELECT SELECT
@@ -179,7 +180,7 @@ LIMIT 1;
user, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[User]) user, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[User])
if err != nil { if err != nil {
if errors.Is(err, pgx.ErrNoRows) { if errors.Is(err, pgx.ErrNoRows) {
return &ErrUserNotFound{Identifier: email} return &ErrUserNotFound{Identifier: email.String()}
} }
return fmt.Errorf("cannot collect user: %w", err) return fmt.Errorf("cannot collect user: %w", err)
@@ -362,11 +363,11 @@ WHERE
` `
args := pgx.StrictNamedArgs{ args := pgx.StrictNamedArgs{
"user_id": u.ID, "user_id": u.ID,
"email_address": u.EmailAddress, "email_address": u.EmailAddress,
"email_address_verified": u.EmailAddressVerified, "email_address_verified": u.EmailAddressVerified,
"saml_subject": u.SAMLSubject, "saml_subject": u.SAMLSubject,
"updated_at": u.UpdatedAt, "updated_at": u.UpdatedAt,
} }
_, err := conn.Exec(ctx, q, args) _, err := conn.Exec(ctx, q, args)

View File

@@ -24,22 +24,23 @@ import (
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
"go.gearno.de/kit/pg" "go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/page"
) )
type ( type (
VendorContact struct { VendorContact struct {
ID gid.GID `db:"id"` ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"` OrganizationID gid.GID `db:"organization_id"`
VendorID gid.GID `db:"vendor_id"` VendorID gid.GID `db:"vendor_id"`
FullName *string `db:"full_name"` FullName *string `db:"full_name"`
Email *string `db:"email"` Email *mail.Addr `db:"email"`
Phone *string `db:"phone"` Phone *string `db:"phone"`
Role *string `db:"role"` Role *string `db:"role"`
SnapshotID *gid.GID `db:"snapshot_id"` SnapshotID *gid.GID `db:"snapshot_id"`
SourceID *gid.GID `db:"source_id"` SourceID *gid.GID `db:"source_id"`
CreatedAt time.Time `db:"created_at"` CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"` UpdatedAt time.Time `db:"updated_at"`
} }
VendorContacts []*VendorContact VendorContacts []*VendorContact

74
pkg/mail/addr.go Normal file
View File

@@ -0,0 +1,74 @@
package mail
import (
"database/sql/driver"
"encoding/json"
"fmt"
"net/mail"
"strings"
)
type Addr string
var Nil Addr
func (a Addr) String() string {
return string(a)
}
func (a *Addr) Domain() string {
return strings.Split(a.String(), "@")[1]
}
func ParseAddr(s string) (Addr, error) {
netMailAddr, err := mail.ParseAddress(s)
if err != nil {
return Nil, fmt.Errorf("cannot parse address %s: %w", s, err)
}
parts := strings.Split(netMailAddr.Address, "@")
if len(parts) != 2 {
return Nil, fmt.Errorf("invalid email address format")
}
return Addr(netMailAddr.Address), nil
}
func (a Addr) Value() (driver.Value, error) {
return a.String(), nil
}
func (a *Addr) Scan(value any) error {
switch v := value.(type) {
case string:
parsed, err := ParseAddr(v)
if err != nil {
return err
}
*a = parsed
default:
return fmt.Errorf("invalid type for mail.Addr: expected string, got %T", value)
}
return nil
}
func (a *Addr) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return fmt.Errorf("cannot unmarshal email string from JSON")
}
parsed, err := ParseAddr(s)
if err != nil {
return fmt.Errorf("cannot parse email address")
}
*a = parsed
return nil
}
func (a Addr) MarshalJSON() ([]byte, error) {
return json.Marshal(a.String())
}

View File

@@ -148,7 +148,7 @@ func (s *ConnectorService) Create(
return fmt.Errorf("cannot parse template JSON: %w", err) return fmt.Errorf("cannot parse template JSON: %w", err)
} }
slackMessage := coredata.NewSlackMessage(s.svc.scope, req.OrganizationID, coredata.SlackMessageTypeWelcome, body, nil) slackMessage := coredata.NewSlackMessage(s.svc.scope, req.OrganizationID, coredata.SlackMessageTypeWelcome, body)
if err := slackMessage.Insert(ctx, conn, s.svc.scope); err != nil { if err := slackMessage.Insert(ctx, conn, s.svc.scope); err != nil {
return fmt.Errorf("cannot insert slack message: %w", err) return fmt.Errorf("cannot insert slack message: %w", err)
} }

View File

@@ -6,7 +6,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"net/mail"
"net/url" "net/url"
"os" "os"
"regexp" "regexp"
@@ -24,6 +23,7 @@ import (
"go.probo.inc/probo/pkg/docgen" "go.probo.inc/probo/pkg/docgen"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/html2pdf" "go.probo.inc/probo/pkg/html2pdf"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/page"
"go.probo.inc/probo/pkg/statelesstoken" "go.probo.inc/probo/pkg/statelesstoken"
"go.probo.inc/probo/pkg/validator" "go.probo.inc/probo/pkg/validator"
@@ -608,7 +608,7 @@ func (s *DocumentService) SignDocumentVersion(
func (s *DocumentService) SignDocumentVersionByEmail( func (s *DocumentService) SignDocumentVersionByEmail(
ctx context.Context, ctx context.Context,
documentVersionID gid.GID, documentVersionID gid.GID,
userEmail string, userEmail mail.Addr,
) (*coredata.DocumentVersionSignature, error) { ) (*coredata.DocumentVersionSignature, error) {
var documentVersionSignature *coredata.DocumentVersionSignature var documentVersionSignature *coredata.DocumentVersionSignature
@@ -887,7 +887,7 @@ func (s *DocumentService) ListSignatures(
func (s *DocumentService) IsVersionSignedByUserEmail( func (s *DocumentService) IsVersionSignedByUserEmail(
ctx context.Context, ctx context.Context,
documentVersionID gid.GID, documentVersionID gid.GID,
userEmail string, userEmail mail.Addr,
) (bool, error) { ) (bool, error) {
documentVersionSignature := &coredata.DocumentVersionSignature{} documentVersionSignature := &coredata.DocumentVersionSignature{}
@@ -1032,7 +1032,7 @@ func (s *DocumentService) BulkSoftDelete(
func (s *DocumentService) RequestExport( func (s *DocumentService) RequestExport(
ctx context.Context, ctx context.Context,
documentIDs []gid.GID, documentIDs []gid.GID,
recipientEmail string, recipientEmail mail.Addr,
recipientName string, recipientName string,
options ExportPDFOptions, options ExportPDFOptions,
) (*coredata.ExportJob, error) { ) (*coredata.ExportJob, error) {
@@ -1043,9 +1043,6 @@ func (s *DocumentService) RequestExport(
if options.WatermarkEmail == nil { if options.WatermarkEmail == nil {
return nil, fmt.Errorf("watermark email is required when with watermark is true") return nil, fmt.Errorf("watermark email is required when with watermark is true")
} }
if _, err := mail.ParseAddress(*options.WatermarkEmail); err != nil {
return nil, fmt.Errorf("invalid email address")
}
} }
err := s.svc.pg.WithTx(ctx, func(conn pg.Conn) error { err := s.svc.pg.WithTx(ctx, func(conn pg.Conn) error {
@@ -1149,7 +1146,7 @@ func (s *DocumentService) GetVersion(
func (s *DocumentService) IsSigned( func (s *DocumentService) IsSigned(
ctx context.Context, ctx context.Context,
documentID gid.GID, documentID gid.GID,
userEmail string, userEmail mail.Addr,
) (bool, error) { ) (bool, error) {
document := &coredata.Document{} document := &coredata.Document{}
@@ -1438,7 +1435,7 @@ func (s *DocumentService) CancelSignatureRequest(
type ExportPDFOptions struct { type ExportPDFOptions struct {
WithWatermark bool WithWatermark bool
WatermarkEmail *string WatermarkEmail *mail.Addr
WithSignatures bool WithSignatures bool
} }
@@ -1695,9 +1692,6 @@ func exportDocumentPDF(
if options.WatermarkEmail == nil { if options.WatermarkEmail == nil {
return nil, fmt.Errorf("watermark email is required with watermark enabled") return nil, fmt.Errorf("watermark email is required with watermark enabled")
} }
if _, err := mail.ParseAddress(*options.WatermarkEmail); err != nil {
return nil, fmt.Errorf("invalid email address")
}
watermarkedPDF, err := watermarkpdf.AddConfidentialWithTimestamp(pdfData, *options.WatermarkEmail) watermarkedPDF, err := watermarkpdf.AddConfidentialWithTimestamp(pdfData, *options.WatermarkEmail)
if err != nil { if err != nil {
@@ -1770,7 +1764,7 @@ func (s *DocumentService) SendExportEmail(
ctx context.Context, ctx context.Context,
fileID gid.GID, fileID gid.GID,
recipientName string, recipientName string,
recipientEmail string, recipientEmail mail.Addr,
) error { ) error {
return s.svc.pg.WithTx( return s.svc.pg.WithTx(
ctx, ctx,

View File

@@ -32,6 +32,7 @@ import (
"go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/html2pdf" "go.probo.inc/probo/pkg/html2pdf"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/page"
"go.probo.inc/probo/pkg/slug" "go.probo.inc/probo/pkg/slug"
"go.probo.inc/probo/pkg/soagen" "go.probo.inc/probo/pkg/soagen"
@@ -97,7 +98,7 @@ func (ufr *UpdateFrameworkRequest) Validate() error {
func (s FrameworkService) RequestExport( func (s FrameworkService) RequestExport(
ctx context.Context, ctx context.Context,
frameworkID gid.GID, frameworkID gid.GID,
recipientEmail string, recipientEmail mail.Addr,
recipientName string, recipientName string,
) (error, *coredata.ExportJob) { ) (error, *coredata.ExportJob) {
var exportJobID gid.GID var exportJobID gid.GID
@@ -725,7 +726,7 @@ func (s FrameworkService) SendExportEmail(
ctx context.Context, ctx context.Context,
fileID gid.GID, fileID gid.GID,
recipientName string, recipientName string,
recipientEmail string, recipientEmail mail.Addr,
) error { ) error {
return s.svc.pg.WithTx( return s.svc.pg.WithTx(
ctx, ctx,

View File

@@ -22,6 +22,7 @@ import (
"go.gearno.de/kit/pg" "go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/page"
"go.probo.inc/probo/pkg/validator" "go.probo.inc/probo/pkg/validator"
) )
@@ -34,8 +35,8 @@ type (
CreatePeopleRequest struct { CreatePeopleRequest struct {
OrganizationID gid.GID OrganizationID gid.GID
FullName string FullName string
PrimaryEmailAddress string PrimaryEmailAddress mail.Addr
AdditionalEmailAddresses []string AdditionalEmailAddresses []mail.Addr
Kind coredata.PeopleKind Kind coredata.PeopleKind
Position *string Position *string
ContractStartDate *time.Time ContractStartDate *time.Time
@@ -46,8 +47,8 @@ type (
ID gid.GID ID gid.GID
Kind *coredata.PeopleKind Kind *coredata.PeopleKind
FullName *string FullName *string
PrimaryEmailAddress *string PrimaryEmailAddress *mail.Addr
AdditionalEmailAddresses *[]string AdditionalEmailAddresses *[]mail.Addr
Position **string Position **string
ContractStartDate **time.Time ContractStartDate **time.Time
ContractEndDate **time.Time ContractEndDate **time.Time
@@ -59,9 +60,9 @@ func (cpr *CreatePeopleRequest) Validate() error {
v.Check(cpr.OrganizationID, "organization_id", validator.Required(), validator.GID(coredata.OrganizationEntityType)) v.Check(cpr.OrganizationID, "organization_id", validator.Required(), validator.GID(coredata.OrganizationEntityType))
v.Check(cpr.FullName, "full_name", validator.SafeTextNoNewLine(NameMaxLength)) v.Check(cpr.FullName, "full_name", validator.SafeTextNoNewLine(NameMaxLength))
v.Check(cpr.PrimaryEmailAddress, "primary_email_address", validator.Required(), validator.NotEmpty(), validator.Email()) v.Check(cpr.PrimaryEmailAddress, "primary_email_address", validator.Required(), validator.NotEmpty())
v.CheckEach(cpr.AdditionalEmailAddresses, "additional_email_addresses", func(index int, item any) { v.CheckEach(cpr.AdditionalEmailAddresses, "additional_email_addresses", func(index int, item any) {
v.Check(item, fmt.Sprintf("additional_email_addresses[%d]", index), validator.Required(), validator.NotEmpty(), validator.Email()) v.Check(item, fmt.Sprintf("additional_email_addresses[%d]", index), validator.Required(), validator.NotEmpty())
}) })
v.Check(cpr.Kind, "kind", validator.Required(), validator.OneOfSlice(coredata.PeopleKinds())) v.Check(cpr.Kind, "kind", validator.Required(), validator.OneOfSlice(coredata.PeopleKinds()))
v.Check(cpr.Position, "position", validator.SafeText(TitleMaxLength)) v.Check(cpr.Position, "position", validator.SafeText(TitleMaxLength))
@@ -77,9 +78,9 @@ func (upr *UpdatePeopleRequest) Validate() error {
v.Check(upr.ID, "id", validator.Required(), validator.GID(coredata.PeopleEntityType)) v.Check(upr.ID, "id", validator.Required(), validator.GID(coredata.PeopleEntityType))
v.Check(upr.Kind, "kind", validator.OneOfSlice(coredata.PeopleKinds())) v.Check(upr.Kind, "kind", validator.OneOfSlice(coredata.PeopleKinds()))
v.Check(upr.FullName, "full_name", validator.SafeTextNoNewLine(NameMaxLength)) v.Check(upr.FullName, "full_name", validator.SafeTextNoNewLine(NameMaxLength))
v.Check(upr.PrimaryEmailAddress, "primary_email_address", validator.NotEmpty(), validator.Email()) v.Check(upr.PrimaryEmailAddress, "primary_email_address", validator.NotEmpty())
v.CheckEach(upr.AdditionalEmailAddresses, "additional_email_addresses", func(index int, item any) { v.CheckEach(upr.AdditionalEmailAddresses, "additional_email_addresses", func(index int, item any) {
v.Check(item, fmt.Sprintf("additional_email_addresses[%d]", index), validator.Required(), validator.NotEmpty(), validator.Email()) v.Check(item, fmt.Sprintf("additional_email_addresses[%d]", index), validator.Required(), validator.NotEmpty())
}) })
v.Check(upr.Position, "position", validator.SafeText(TitleMaxLength)) v.Check(upr.Position, "position", validator.SafeText(TitleMaxLength))
v.Check(upr.ContractStartDate, "contract_start_date", validator.Before(upr.ContractEndDate)) v.Check(upr.ContractStartDate, "contract_start_date", validator.Before(upr.ContractEndDate))
@@ -110,7 +111,7 @@ func (s PeopleService) Get(
func (s PeopleService) GetByEmailAndOrganizationID( func (s PeopleService) GetByEmailAndOrganizationID(
ctx context.Context, ctx context.Context,
primaryEmailAddress string, primaryEmailAddress mail.Addr,
organizationID gid.GID, organizationID gid.GID,
) (*coredata.People, error) { ) (*coredata.People, error) {
people := &coredata.People{} people := &coredata.People{}

View File

@@ -31,6 +31,7 @@ import (
"go.probo.inc/probo/pkg/crypto/cipher" "go.probo.inc/probo/pkg/crypto/cipher"
"go.probo.inc/probo/pkg/filemanager" "go.probo.inc/probo/pkg/filemanager"
"go.probo.inc/probo/pkg/filevalidation" "go.probo.inc/probo/pkg/filevalidation"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/html2pdf" "go.probo.inc/probo/pkg/html2pdf"
"go.probo.inc/probo/pkg/slack" "go.probo.inc/probo/pkg/slack"
@@ -44,7 +45,7 @@ const (
type ExportService interface { type ExportService interface {
BuildAndUploadExport(ctx context.Context, exportJobID gid.GID) (*coredata.ExportJob, error) BuildAndUploadExport(ctx context.Context, exportJobID gid.GID) (*coredata.ExportJob, error)
SendExportEmail(ctx context.Context, fileID gid.GID, recipientName, recipientEmail string) error SendExportEmail(ctx context.Context, fileID gid.GID, recipientName string, recipientEmail mail.Addr) error
} }
type ( type (

View File

@@ -24,6 +24,7 @@ import (
"go.probo.inc/probo/packages/emails" "go.probo.inc/probo/packages/emails"
"go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/page"
"go.probo.inc/probo/pkg/statelesstoken" "go.probo.inc/probo/pkg/statelesstoken"
"go.probo.inc/probo/pkg/validator" "go.probo.inc/probo/pkg/validator"
@@ -36,7 +37,7 @@ type (
CreateTrustCenterAccessRequest struct { CreateTrustCenterAccessRequest struct {
TrustCenterID gid.GID TrustCenterID gid.GID
Email string Email mail.Addr
Name string Name string
} }
@@ -55,8 +56,8 @@ type (
} }
TrustCenterAccessData struct { TrustCenterAccessData struct {
TrustCenterID gid.GID `json:"trust_center_id"` TrustCenterID gid.GID `json:"trust_center_id"`
Email string `json:"email"` Email mail.Addr `json:"email"`
} }
) )
@@ -64,7 +65,7 @@ func (ctcar *CreateTrustCenterAccessRequest) Validate() error {
v := validator.New() v := validator.New()
v.Check(ctcar.TrustCenterID, "trust_center_id", validator.Required(), validator.GID(coredata.TrustCenterEntityType)) v.Check(ctcar.TrustCenterID, "trust_center_id", validator.Required(), validator.GID(coredata.TrustCenterEntityType))
v.Check(ctcar.Email, "email", validator.Required(), validator.Email()) v.Check(ctcar.Email, "email", validator.Required(), validator.NotEmpty())
v.Check(ctcar.Name, "name", validator.SafeTextNoNewLine(TitleMaxLength)) v.Check(ctcar.Name, "name", validator.SafeTextNoNewLine(TitleMaxLength))
return v.Error() return v.Error()
@@ -469,7 +470,7 @@ func (s TrustCenterAccessService) sendTrustCenterAccessEmail(
ctx context.Context, ctx context.Context,
tx pg.Conn, tx pg.Conn,
name string, name string,
email string, email mail.Addr,
companyName string, companyName string,
accessURL string, accessURL string,
) error { ) error {

View File

@@ -22,6 +22,7 @@ import (
"go.gearno.de/kit/pg" "go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/page"
"go.probo.inc/probo/pkg/validator" "go.probo.inc/probo/pkg/validator"
) )
@@ -34,7 +35,7 @@ type (
CreateVendorContactRequest struct { CreateVendorContactRequest struct {
VendorID gid.GID VendorID gid.GID
FullName *string FullName *string
Email *string Email *mail.Addr
Phone *string Phone *string
Role *string Role *string
} }
@@ -42,7 +43,7 @@ type (
UpdateVendorContactRequest struct { UpdateVendorContactRequest struct {
ID gid.GID ID gid.GID
FullName **string FullName **string
Email **string Email **mail.Addr
Phone **string Phone **string
Role **string Role **string
} }
@@ -53,7 +54,6 @@ func (cvcr *CreateVendorContactRequest) Validate() error {
v.Check(cvcr.VendorID, "vendor_id", validator.Required(), validator.GID(coredata.VendorEntityType)) v.Check(cvcr.VendorID, "vendor_id", validator.Required(), validator.GID(coredata.VendorEntityType))
v.Check(cvcr.FullName, "fullName", validator.SafeTextNoNewLine(TitleMaxLength)) v.Check(cvcr.FullName, "fullName", validator.SafeTextNoNewLine(TitleMaxLength))
v.Check(cvcr.Email, "email", validator.Email())
v.Check(cvcr.Phone, "phone", validator.SafeText(NameMaxLength)) v.Check(cvcr.Phone, "phone", validator.SafeText(NameMaxLength))
v.Check(cvcr.Role, "role", validator.SafeText(TitleMaxLength)) v.Check(cvcr.Role, "role", validator.SafeText(TitleMaxLength))
@@ -65,7 +65,6 @@ func (uvcr *UpdateVendorContactRequest) Validate() error {
v.Check(uvcr.ID, "id", validator.Required(), validator.GID(coredata.VendorContactEntityType)) v.Check(uvcr.ID, "id", validator.Required(), validator.GID(coredata.VendorContactEntityType))
v.Check(uvcr.FullName, "fullName", validator.SafeTextNoNewLine(TitleMaxLength)) v.Check(uvcr.FullName, "fullName", validator.SafeTextNoNewLine(TitleMaxLength))
v.Check(uvcr.Email, "email", validator.Email())
v.Check(uvcr.Phone, "phone", validator.SafeText(NameMaxLength)) v.Check(uvcr.Phone, "phone", validator.SafeText(NameMaxLength))
v.Check(uvcr.Role, "role", validator.SafeText(TitleMaxLength)) v.Check(uvcr.Role, "role", validator.SafeText(TitleMaxLength))

View File

@@ -37,3 +37,6 @@ models:
BigInt: BigInt:
model: model:
- "go.probo.inc/probo/pkg/server/gqlutils/types/bigint.BigIntScalar" - "go.probo.inc/probo/pkg/server/gqlutils/types/bigint.BigIntScalar"
EmailAddr:
model:
- "go.probo.inc/probo/pkg/server/gqlutils/types/mail.AddrScalar"

View File

@@ -19,6 +19,7 @@ scalar Datetime
scalar Upload scalar Upload
scalar Duration scalar Duration
scalar BigInt scalar BigInt
scalar EmailAddr
# Interfaces # Interfaces
interface Node { interface Node {
@@ -99,7 +100,8 @@ enum MembershipRole
EMPLOYEE EMPLOYEE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleEmployee") @goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleEmployee")
VIEWER @goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleViewer") VIEWER @goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleViewer")
AUDITOR @goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleAuditor") AUDITOR
@goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleAuditor")
} }
enum APIRole @goModel(model: "go.probo.inc/probo/pkg/coredata.APIRole") { enum APIRole @goModel(model: "go.probo.inc/probo/pkg/coredata.APIRole") {
@@ -1687,7 +1689,7 @@ type Organization implements Node {
type User implements Node { type User implements Node {
id: ID! id: ID!
fullName: String! fullName: String!
email: String! email: EmailAddr!
createdAt: Datetime! createdAt: Datetime!
updatedAt: Datetime! updatedAt: Datetime!
} }
@@ -1698,7 +1700,7 @@ type Membership implements Node {
organizationID: ID! organizationID: ID!
role: MembershipRole! role: MembershipRole!
fullName: String! fullName: String!
emailAddress: String! emailAddress: EmailAddr!
authMethod: UserAuthMethod! @goField(forceResolver: true) authMethod: UserAuthMethod! @goField(forceResolver: true)
createdAt: Datetime! createdAt: Datetime!
updatedAt: Datetime! updatedAt: Datetime!
@@ -1706,7 +1708,7 @@ type Membership implements Node {
type Invitation implements Node { type Invitation implements Node {
id: ID! id: ID!
email: String! email: EmailAddr!
fullName: String! fullName: String!
role: MembershipRole! role: MembershipRole!
status: InvitationStatus! status: InvitationStatus!
@@ -1737,8 +1739,8 @@ type SlackConnectionEdge {
type People implements Node { type People implements Node {
id: ID! id: ID!
fullName: String! fullName: String!
primaryEmailAddress: String! primaryEmailAddress: EmailAddr!
additionalEmailAddresses: [String!]! additionalEmailAddresses: [EmailAddr!]!
kind: PeopleKind! kind: PeopleKind!
position: String position: String
contractStartDate: Datetime contractStartDate: Datetime
@@ -1841,7 +1843,7 @@ type VendorContact implements Node {
id: ID! id: ID!
vendor: Vendor! @goField(forceResolver: true) vendor: Vendor! @goField(forceResolver: true)
fullName: String fullName: String
email: String email: EmailAddr
phone: String phone: String
role: String role: String
createdAt: Datetime! createdAt: Datetime!
@@ -2342,7 +2344,7 @@ type TrustCenterEdge {
type TrustCenterAccess implements Node { type TrustCenterAccess implements Node {
id: ID! id: ID!
email: String! email: EmailAddr!
name: String! name: String!
active: Boolean! active: Boolean!
hasAcceptedNonDisclosureAgreement: Boolean! hasAcceptedNonDisclosureAgreement: Boolean!
@@ -3185,7 +3187,7 @@ input DeleteTrustCenterNDAInput {
input CreateTrustCenterAccessInput { input CreateTrustCenterAccessInput {
trustCenterId: ID! trustCenterId: ID!
email: String! email: EmailAddr!
name: String! name: String!
active: Boolean! active: Boolean!
} }
@@ -3306,7 +3308,7 @@ input DeleteVendorInput {
input CreateVendorContactInput { input CreateVendorContactInput {
vendorId: ID! vendorId: ID!
fullName: String fullName: String
email: String email: EmailAddr
phone: String phone: String
role: String role: String
} }
@@ -3314,7 +3316,7 @@ input CreateVendorContactInput {
input UpdateVendorContactInput { input UpdateVendorContactInput {
id: ID! id: ID!
fullName: String @goField(omittable: true) fullName: String @goField(omittable: true)
email: String @goField(omittable: true) email: EmailAddr @goField(omittable: true)
phone: String @goField(omittable: true) phone: String @goField(omittable: true)
role: String @goField(omittable: true) role: String @goField(omittable: true)
} }
@@ -3346,8 +3348,8 @@ input DeleteVendorServiceInput {
input CreatePeopleInput { input CreatePeopleInput {
organizationId: ID! organizationId: ID!
fullName: String! fullName: String!
primaryEmailAddress: String! primaryEmailAddress: EmailAddr!
additionalEmailAddresses: [String!] additionalEmailAddresses: [EmailAddr!]
kind: PeopleKind! kind: PeopleKind!
position: String position: String
contractStartDate: Datetime contractStartDate: Datetime
@@ -3357,8 +3359,8 @@ input CreatePeopleInput {
input UpdatePeopleInput { input UpdatePeopleInput {
id: ID! id: ID!
fullName: String fullName: String
primaryEmailAddress: String primaryEmailAddress: EmailAddr
additionalEmailAddresses: [String!] additionalEmailAddresses: [EmailAddr!]
kind: PeopleKind kind: PeopleKind
position: String @goField(omittable: true) position: String @goField(omittable: true)
contractStartDate: Datetime @goField(omittable: true) contractStartDate: Datetime @goField(omittable: true)
@@ -3646,7 +3648,7 @@ input UpdateDocumentInput {
input ExportDocumentVersionPDFInput { input ExportDocumentVersionPDFInput {
documentVersionId: ID! documentVersionId: ID!
withWatermark: Boolean! withWatermark: Boolean!
watermarkEmail: String watermarkEmail: EmailAddr
withSignatures: Boolean! withSignatures: Boolean!
} }
@@ -3684,7 +3686,7 @@ input ConfirmEmailInput {
input InviteUserInput { input InviteUserInput {
organizationId: ID! organizationId: ID!
email: String! email: EmailAddr!
fullName: String! fullName: String!
role: MembershipRole! role: MembershipRole!
createPeople: Boolean! createPeople: Boolean!
@@ -4459,7 +4461,7 @@ input BulkDeleteDocumentsInput {
input BulkExportDocumentsInput { input BulkExportDocumentsInput {
documentIds: [ID!]! documentIds: [ID!]!
withWatermark: Boolean! withWatermark: Boolean!
watermarkEmail: String watermarkEmail: EmailAddr
withSignatures: Boolean! withSignatures: Boolean!
} }

View File

@@ -18,11 +18,13 @@ import (
"github.com/vektah/gqlparser/v2/ast" "github.com/vektah/gqlparser/v2/ast"
"go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/page"
"go.probo.inc/probo/pkg/server/api/console/v1/types" "go.probo.inc/probo/pkg/server/api/console/v1/types"
"go.probo.inc/probo/pkg/server/gqlutils/types/bigint" "go.probo.inc/probo/pkg/server/gqlutils/types/bigint"
"go.probo.inc/probo/pkg/server/gqlutils/types/cursor" "go.probo.inc/probo/pkg/server/gqlutils/types/cursor"
gid1 "go.probo.inc/probo/pkg/server/gqlutils/types/gid" gid1 "go.probo.inc/probo/pkg/server/gqlutils/types/gid"
mail1 "go.probo.inc/probo/pkg/server/gqlutils/types/mail"
) )
// region ************************** generated!.gotpl ************************** // region ************************** generated!.gotpl **************************
@@ -9573,6 +9575,7 @@ scalar Datetime
scalar Upload scalar Upload
scalar Duration scalar Duration
scalar BigInt scalar BigInt
scalar EmailAddr
# Interfaces # Interfaces
interface Node { interface Node {
@@ -9653,7 +9656,8 @@ enum MembershipRole
EMPLOYEE EMPLOYEE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleEmployee") @goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleEmployee")
VIEWER @goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleViewer") VIEWER @goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleViewer")
AUDITOR @goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleAuditor") AUDITOR
@goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleAuditor")
} }
enum APIRole @goModel(model: "go.probo.inc/probo/pkg/coredata.APIRole") { enum APIRole @goModel(model: "go.probo.inc/probo/pkg/coredata.APIRole") {
@@ -11241,7 +11245,7 @@ type Organization implements Node {
type User implements Node { type User implements Node {
id: ID! id: ID!
fullName: String! fullName: String!
email: String! email: EmailAddr!
createdAt: Datetime! createdAt: Datetime!
updatedAt: Datetime! updatedAt: Datetime!
} }
@@ -11252,7 +11256,7 @@ type Membership implements Node {
organizationID: ID! organizationID: ID!
role: MembershipRole! role: MembershipRole!
fullName: String! fullName: String!
emailAddress: String! emailAddress: EmailAddr!
authMethod: UserAuthMethod! @goField(forceResolver: true) authMethod: UserAuthMethod! @goField(forceResolver: true)
createdAt: Datetime! createdAt: Datetime!
updatedAt: Datetime! updatedAt: Datetime!
@@ -11260,7 +11264,7 @@ type Membership implements Node {
type Invitation implements Node { type Invitation implements Node {
id: ID! id: ID!
email: String! email: EmailAddr!
fullName: String! fullName: String!
role: MembershipRole! role: MembershipRole!
status: InvitationStatus! status: InvitationStatus!
@@ -11291,8 +11295,8 @@ type SlackConnectionEdge {
type People implements Node { type People implements Node {
id: ID! id: ID!
fullName: String! fullName: String!
primaryEmailAddress: String! primaryEmailAddress: EmailAddr!
additionalEmailAddresses: [String!]! additionalEmailAddresses: [EmailAddr!]!
kind: PeopleKind! kind: PeopleKind!
position: String position: String
contractStartDate: Datetime contractStartDate: Datetime
@@ -11395,7 +11399,7 @@ type VendorContact implements Node {
id: ID! id: ID!
vendor: Vendor! @goField(forceResolver: true) vendor: Vendor! @goField(forceResolver: true)
fullName: String fullName: String
email: String email: EmailAddr
phone: String phone: String
role: String role: String
createdAt: Datetime! createdAt: Datetime!
@@ -11896,7 +11900,7 @@ type TrustCenterEdge {
type TrustCenterAccess implements Node { type TrustCenterAccess implements Node {
id: ID! id: ID!
email: String! email: EmailAddr!
name: String! name: String!
active: Boolean! active: Boolean!
hasAcceptedNonDisclosureAgreement: Boolean! hasAcceptedNonDisclosureAgreement: Boolean!
@@ -12739,7 +12743,7 @@ input DeleteTrustCenterNDAInput {
input CreateTrustCenterAccessInput { input CreateTrustCenterAccessInput {
trustCenterId: ID! trustCenterId: ID!
email: String! email: EmailAddr!
name: String! name: String!
active: Boolean! active: Boolean!
} }
@@ -12860,7 +12864,7 @@ input DeleteVendorInput {
input CreateVendorContactInput { input CreateVendorContactInput {
vendorId: ID! vendorId: ID!
fullName: String fullName: String
email: String email: EmailAddr
phone: String phone: String
role: String role: String
} }
@@ -12868,7 +12872,7 @@ input CreateVendorContactInput {
input UpdateVendorContactInput { input UpdateVendorContactInput {
id: ID! id: ID!
fullName: String @goField(omittable: true) fullName: String @goField(omittable: true)
email: String @goField(omittable: true) email: EmailAddr @goField(omittable: true)
phone: String @goField(omittable: true) phone: String @goField(omittable: true)
role: String @goField(omittable: true) role: String @goField(omittable: true)
} }
@@ -12900,8 +12904,8 @@ input DeleteVendorServiceInput {
input CreatePeopleInput { input CreatePeopleInput {
organizationId: ID! organizationId: ID!
fullName: String! fullName: String!
primaryEmailAddress: String! primaryEmailAddress: EmailAddr!
additionalEmailAddresses: [String!] additionalEmailAddresses: [EmailAddr!]
kind: PeopleKind! kind: PeopleKind!
position: String position: String
contractStartDate: Datetime contractStartDate: Datetime
@@ -12911,8 +12915,8 @@ input CreatePeopleInput {
input UpdatePeopleInput { input UpdatePeopleInput {
id: ID! id: ID!
fullName: String fullName: String
primaryEmailAddress: String primaryEmailAddress: EmailAddr
additionalEmailAddresses: [String!] additionalEmailAddresses: [EmailAddr!]
kind: PeopleKind kind: PeopleKind
position: String @goField(omittable: true) position: String @goField(omittable: true)
contractStartDate: Datetime @goField(omittable: true) contractStartDate: Datetime @goField(omittable: true)
@@ -13200,7 +13204,7 @@ input UpdateDocumentInput {
input ExportDocumentVersionPDFInput { input ExportDocumentVersionPDFInput {
documentVersionId: ID! documentVersionId: ID!
withWatermark: Boolean! withWatermark: Boolean!
watermarkEmail: String watermarkEmail: EmailAddr
withSignatures: Boolean! withSignatures: Boolean!
} }
@@ -13238,7 +13242,7 @@ input ConfirmEmailInput {
input InviteUserInput { input InviteUserInput {
organizationId: ID! organizationId: ID!
email: String! email: EmailAddr!
fullName: String! fullName: String!
role: MembershipRole! role: MembershipRole!
createPeople: Boolean! createPeople: Boolean!
@@ -14013,7 +14017,7 @@ input BulkDeleteDocumentsInput {
input BulkExportDocumentsInput { input BulkExportDocumentsInput {
documentIds: [ID!]! documentIds: [ID!]!
withWatermark: Boolean! withWatermark: Boolean!
watermarkEmail: String watermarkEmail: EmailAddr
withSignatures: Boolean! withSignatures: Boolean!
} }
@@ -28689,7 +28693,7 @@ func (ec *executionContext) _Invitation_email(ctx context.Context, field graphql
return obj.Email, nil return obj.Email, nil
}, },
nil, nil,
ec.marshalNString2string, ec.marshalNEmailAddr2goᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr,
true, true,
true, true,
) )
@@ -28702,7 +28706,7 @@ func (ec *executionContext) fieldContext_Invitation_email(_ context.Context, fie
IsMethod: false, IsMethod: false,
IsResolver: false, IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields") return nil, errors.New("field of type EmailAddr does not have child fields")
}, },
} }
return fc, nil return fc, nil
@@ -30443,7 +30447,7 @@ func (ec *executionContext) _Membership_emailAddress(ctx context.Context, field
return obj.EmailAddress, nil return obj.EmailAddress, nil
}, },
nil, nil,
ec.marshalNString2string, ec.marshalNEmailAddr2goᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr,
true, true,
true, true,
) )
@@ -30456,7 +30460,7 @@ func (ec *executionContext) fieldContext_Membership_emailAddress(_ context.Conte
IsMethod: false, IsMethod: false,
IsResolver: false, IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields") return nil, errors.New("field of type EmailAddr does not have child fields")
}, },
} }
return fc, nil return fc, nil
@@ -40421,7 +40425,7 @@ func (ec *executionContext) _People_primaryEmailAddress(ctx context.Context, fie
return obj.PrimaryEmailAddress, nil return obj.PrimaryEmailAddress, nil
}, },
nil, nil,
ec.marshalNString2string, ec.marshalNEmailAddr2goᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr,
true, true,
true, true,
) )
@@ -40434,7 +40438,7 @@ func (ec *executionContext) fieldContext_People_primaryEmailAddress(_ context.Co
IsMethod: false, IsMethod: false,
IsResolver: false, IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields") return nil, errors.New("field of type EmailAddr does not have child fields")
}, },
} }
return fc, nil return fc, nil
@@ -40450,7 +40454,7 @@ func (ec *executionContext) _People_additionalEmailAddresses(ctx context.Context
return obj.AdditionalEmailAddresses, nil return obj.AdditionalEmailAddresses, nil
}, },
nil, nil,
ec.marshalNString2ᚕstringᚄ, ec.marshalNEmailAddr2ᚕᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddrᚄ,
true, true,
true, true,
) )
@@ -40463,7 +40467,7 @@ func (ec *executionContext) fieldContext_People_additionalEmailAddresses(_ conte
IsMethod: false, IsMethod: false,
IsResolver: false, IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields") return nil, errors.New("field of type EmailAddr does not have child fields")
}, },
} }
return fc, nil return fc, nil
@@ -46593,7 +46597,7 @@ func (ec *executionContext) _TrustCenterAccess_email(ctx context.Context, field
return obj.Email, nil return obj.Email, nil
}, },
nil, nil,
ec.marshalNString2string, ec.marshalNEmailAddr2goᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr,
true, true,
true, true,
) )
@@ -46606,7 +46610,7 @@ func (ec *executionContext) fieldContext_TrustCenterAccess_email(_ context.Conte
IsMethod: false, IsMethod: false,
IsResolver: false, IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields") return nil, errors.New("field of type EmailAddr does not have child fields")
}, },
} }
return fc, nil return fc, nil
@@ -50525,7 +50529,7 @@ func (ec *executionContext) _User_email(ctx context.Context, field graphql.Colle
return obj.Email, nil return obj.Email, nil
}, },
nil, nil,
ec.marshalNString2string, ec.marshalNEmailAddr2goᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr,
true, true,
true, true,
) )
@@ -50538,7 +50542,7 @@ func (ec *executionContext) fieldContext_User_email(_ context.Context, field gra
IsMethod: false, IsMethod: false,
IsResolver: false, IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields") return nil, errors.New("field of type EmailAddr does not have child fields")
}, },
} }
return fc, nil return fc, nil
@@ -52953,7 +52957,7 @@ func (ec *executionContext) _VendorContact_email(ctx context.Context, field grap
return obj.Email, nil return obj.Email, nil
}, },
nil, nil,
ec.marshalOString2ᚖstring, ec.marshalOEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr,
true, true,
false, false,
) )
@@ -52966,7 +52970,7 @@ func (ec *executionContext) fieldContext_VendorContact_email(_ context.Context,
IsMethod: false, IsMethod: false,
IsResolver: false, IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields") return nil, errors.New("field of type EmailAddr does not have child fields")
}, },
} }
return fc, nil return fc, nil
@@ -56539,7 +56543,7 @@ func (ec *executionContext) unmarshalInputBulkExportDocumentsInput(ctx context.C
it.WithWatermark = data it.WithWatermark = data
case "watermarkEmail": case "watermarkEmail":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("watermarkEmail")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("watermarkEmail"))
data, err := ec.unmarshalOString2ᚖstring(ctx, v) data, err := ec.unmarshalOEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, v)
if err != nil { if err != nil {
return it, err return it, err
} }
@@ -57848,14 +57852,14 @@ func (ec *executionContext) unmarshalInputCreatePeopleInput(ctx context.Context,
it.FullName = data it.FullName = data
case "primaryEmailAddress": case "primaryEmailAddress":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("primaryEmailAddress")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("primaryEmailAddress"))
data, err := ec.unmarshalNString2string(ctx, v) data, err := ec.unmarshalNEmailAddr2goᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, v)
if err != nil { if err != nil {
return it, err return it, err
} }
it.PrimaryEmailAddress = data it.PrimaryEmailAddress = data
case "additionalEmailAddresses": case "additionalEmailAddresses":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("additionalEmailAddresses")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("additionalEmailAddresses"))
data, err := ec.unmarshalOString2ᚕstringᚄ(ctx, v) data, err := ec.unmarshalOEmailAddr2ᚕᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddrᚄ(ctx, v)
if err != nil { if err != nil {
return it, err return it, err
} }
@@ -58497,7 +58501,7 @@ func (ec *executionContext) unmarshalInputCreateTrustCenterAccessInput(ctx conte
it.TrustCenterID = data it.TrustCenterID = data
case "email": case "email":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email"))
data, err := ec.unmarshalNString2string(ctx, v) data, err := ec.unmarshalNEmailAddr2goᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, v)
if err != nil { if err != nil {
return it, err return it, err
} }
@@ -58662,7 +58666,7 @@ func (ec *executionContext) unmarshalInputCreateVendorContactInput(ctx context.C
it.FullName = data it.FullName = data
case "email": case "email":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email"))
data, err := ec.unmarshalOString2ᚖstring(ctx, v) data, err := ec.unmarshalOEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, v)
if err != nil { if err != nil {
return it, err return it, err
} }
@@ -60475,7 +60479,7 @@ func (ec *executionContext) unmarshalInputExportDocumentVersionPDFInput(ctx cont
it.WithWatermark = data it.WithWatermark = data
case "watermarkEmail": case "watermarkEmail":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("watermarkEmail")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("watermarkEmail"))
data, err := ec.unmarshalOString2ᚖstring(ctx, v) data, err := ec.unmarshalOEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, v)
if err != nil { if err != nil {
return it, err return it, err
} }
@@ -60896,7 +60900,7 @@ func (ec *executionContext) unmarshalInputInviteUserInput(ctx context.Context, o
it.OrganizationID = data it.OrganizationID = data
case "email": case "email":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email"))
data, err := ec.unmarshalNString2string(ctx, v) data, err := ec.unmarshalNEmailAddr2goᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, v)
if err != nil { if err != nil {
return it, err return it, err
} }
@@ -62825,14 +62829,14 @@ func (ec *executionContext) unmarshalInputUpdatePeopleInput(ctx context.Context,
it.FullName = data it.FullName = data
case "primaryEmailAddress": case "primaryEmailAddress":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("primaryEmailAddress")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("primaryEmailAddress"))
data, err := ec.unmarshalOString2ᚖstring(ctx, v) data, err := ec.unmarshalOEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, v)
if err != nil { if err != nil {
return it, err return it, err
} }
it.PrimaryEmailAddress = data it.PrimaryEmailAddress = data
case "additionalEmailAddresses": case "additionalEmailAddresses":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("additionalEmailAddresses")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("additionalEmailAddresses"))
data, err := ec.unmarshalOString2ᚕstringᚄ(ctx, v) data, err := ec.unmarshalOEmailAddr2ᚕᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddrᚄ(ctx, v)
if err != nil { if err != nil {
return it, err return it, err
} }
@@ -63564,7 +63568,7 @@ func (ec *executionContext) unmarshalInputUpdateVendorContactInput(ctx context.C
it.FullName = graphql.OmittableOf(data) it.FullName = graphql.OmittableOf(data)
case "email": case "email":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email"))
data, err := ec.unmarshalOString2ᚖstring(ctx, v) data, err := ec.unmarshalOEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, v)
if err != nil { if err != nil {
return it, err return it, err
} }
@@ -86860,6 +86864,74 @@ var (
} }
) )
func (ec *executionContext) unmarshalNEmailAddr2goᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx context.Context, v any) (mail.Addr, error) {
res, err := mail1.UnmarshalAddrScalar(v)
return res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) marshalNEmailAddr2goᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx context.Context, sel ast.SelectionSet, v mail.Addr) graphql.Marshaler {
_ = sel
res := mail1.MarshalAddrScalar(v)
if res == graphql.Null {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "the requested element is null which the schema does not allow")
}
}
return res
}
func (ec *executionContext) unmarshalNEmailAddr2ᚕᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddrᚄ(ctx context.Context, v any) ([]*mail.Addr, error) {
var vSlice []any
vSlice = graphql.CoerceList(v)
var err error
res := make([]*mail.Addr, len(vSlice))
for i := range vSlice {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i))
res[i], err = ec.unmarshalNEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, vSlice[i])
if err != nil {
return nil, err
}
}
return res, nil
}
func (ec *executionContext) marshalNEmailAddr2ᚕᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddrᚄ(ctx context.Context, sel ast.SelectionSet, v []*mail.Addr) graphql.Marshaler {
ret := make(graphql.Array, len(v))
for i := range v {
ret[i] = ec.marshalNEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, sel, v[i])
}
for _, e := range ret {
if e == graphql.Null {
return graphql.Null
}
}
return ret
}
func (ec *executionContext) unmarshalNEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx context.Context, v any) (*mail.Addr, error) {
res, err := mail1.UnmarshalAddrScalar(v)
return &res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) marshalNEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx context.Context, sel ast.SelectionSet, v *mail.Addr) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "the requested element is null which the schema does not allow")
}
return graphql.Null
}
_ = sel
res := mail1.MarshalAddrScalar(*v)
if res == graphql.Null {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "the requested element is null which the schema does not allow")
}
}
return res
}
func (ec *executionContext) unmarshalNEnableSAMLInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐEnableSAMLInput(ctx context.Context, v any) (types.EnableSAMLInput, error) { func (ec *executionContext) unmarshalNEnableSAMLInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐEnableSAMLInput(ctx context.Context, v any) (types.EnableSAMLInput, error) {
res, err := ec.unmarshalInputEnableSAMLInput(ctx, v) res, err := ec.unmarshalInputEnableSAMLInput(ctx, v)
return res, graphql.ErrorOnPath(ctx, err) return res, graphql.ErrorOnPath(ctx, err)
@@ -92570,6 +92642,60 @@ func (ec *executionContext) marshalODuration2ᚖtimeᚐDuration(ctx context.Cont
return res return res
} }
func (ec *executionContext) unmarshalOEmailAddr2ᚕᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddrᚄ(ctx context.Context, v any) ([]*mail.Addr, error) {
if v == nil {
return nil, nil
}
var vSlice []any
vSlice = graphql.CoerceList(v)
var err error
res := make([]*mail.Addr, len(vSlice))
for i := range vSlice {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i))
res[i], err = ec.unmarshalNEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, vSlice[i])
if err != nil {
return nil, err
}
}
return res, nil
}
func (ec *executionContext) marshalOEmailAddr2ᚕᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddrᚄ(ctx context.Context, sel ast.SelectionSet, v []*mail.Addr) graphql.Marshaler {
if v == nil {
return graphql.Null
}
ret := make(graphql.Array, len(v))
for i := range v {
ret[i] = ec.marshalNEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, sel, v[i])
}
for _, e := range ret {
if e == graphql.Null {
return graphql.Null
}
}
return ret
}
func (ec *executionContext) unmarshalOEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx context.Context, v any) (*mail.Addr, error) {
if v == nil {
return nil, nil
}
res, err := mail1.UnmarshalAddrScalar(v)
return &res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) marshalOEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx context.Context, sel ast.SelectionSet, v *mail.Addr) graphql.Marshaler {
if v == nil {
return graphql.Null
}
_ = sel
_ = ctx
res := mail1.MarshalAddrScalar(*v)
return res
}
func (ec *executionContext) unmarshalOEvidenceOrder2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐEvidenceOrderBy(ctx context.Context, v any) (*types.EvidenceOrderBy, error) { func (ec *executionContext) unmarshalOEvidenceOrder2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐEvidenceOrderBy(ctx context.Context, v any) (*types.EvidenceOrderBy, error) {
if v == nil { if v == nil {
return nil, nil return nil, nil

View File

@@ -17,6 +17,7 @@ package types
import ( import (
"go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/page"
) )
@@ -64,11 +65,16 @@ func NewPeopleEdge(p *coredata.People, orderBy coredata.PeopleOrderField) *Peopl
} }
func NewPeople(p *coredata.People) *People { func NewPeople(p *coredata.People) *People {
var emailAddresses []*mail.Addr
for _, emailAddress := range p.AdditionalEmailAddresses {
emailAddresses = append(emailAddresses, &emailAddress)
}
return &People{ return &People{
ID: p.ID, ID: p.ID,
FullName: p.FullName, FullName: p.FullName,
PrimaryEmailAddress: p.PrimaryEmailAddress, PrimaryEmailAddress: p.PrimaryEmailAddress,
AdditionalEmailAddresses: p.AdditionalEmailAddresses, AdditionalEmailAddresses: emailAddresses,
Kind: p.Kind, Kind: p.Kind,
Position: p.Position, Position: p.Position,
ContractStartDate: p.ContractStartDate, ContractStartDate: p.ContractStartDate,

View File

@@ -12,6 +12,7 @@ import (
"github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql"
"go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/page"
) )
@@ -105,10 +106,10 @@ type BulkDeleteDocumentsPayload struct {
} }
type BulkExportDocumentsInput struct { type BulkExportDocumentsInput struct {
DocumentIds []gid.GID `json:"documentIds"` DocumentIds []gid.GID `json:"documentIds"`
WithWatermark bool `json:"withWatermark"` WithWatermark bool `json:"withWatermark"`
WatermarkEmail *string `json:"watermarkEmail,omitempty"` WatermarkEmail *mail.Addr `json:"watermarkEmail,omitempty"`
WithSignatures bool `json:"withSignatures"` WithSignatures bool `json:"withSignatures"`
} }
type BulkExportDocumentsPayload struct { type BulkExportDocumentsPayload struct {
@@ -419,8 +420,8 @@ type CreateOrganizationPayload struct {
type CreatePeopleInput struct { type CreatePeopleInput struct {
OrganizationID gid.GID `json:"organizationId"` OrganizationID gid.GID `json:"organizationId"`
FullName string `json:"fullName"` FullName string `json:"fullName"`
PrimaryEmailAddress string `json:"primaryEmailAddress"` PrimaryEmailAddress mail.Addr `json:"primaryEmailAddress"`
AdditionalEmailAddresses []string `json:"additionalEmailAddresses,omitempty"` AdditionalEmailAddresses []*mail.Addr `json:"additionalEmailAddresses,omitempty"`
Kind coredata.PeopleKind `json:"kind"` Kind coredata.PeopleKind `json:"kind"`
Position *string `json:"position,omitempty"` Position *string `json:"position,omitempty"`
ContractStartDate *time.Time `json:"contractStartDate,omitempty"` ContractStartDate *time.Time `json:"contractStartDate,omitempty"`
@@ -551,10 +552,10 @@ type CreateTaskPayload struct {
} }
type CreateTrustCenterAccessInput struct { type CreateTrustCenterAccessInput struct {
TrustCenterID gid.GID `json:"trustCenterId"` TrustCenterID gid.GID `json:"trustCenterId"`
Email string `json:"email"` Email mail.Addr `json:"email"`
Name string `json:"name"` Name string `json:"name"`
Active bool `json:"active"` Active bool `json:"active"`
} }
type CreateTrustCenterAccessPayload struct { type CreateTrustCenterAccessPayload struct {
@@ -586,11 +587,11 @@ type CreateTrustCenterReferencePayload struct {
} }
type CreateVendorContactInput struct { type CreateVendorContactInput struct {
VendorID gid.GID `json:"vendorId"` VendorID gid.GID `json:"vendorId"`
FullName *string `json:"fullName,omitempty"` FullName *string `json:"fullName,omitempty"`
Email *string `json:"email,omitempty"` Email *mail.Addr `json:"email,omitempty"`
Phone *string `json:"phone,omitempty"` Phone *string `json:"phone,omitempty"`
Role *string `json:"role,omitempty"` Role *string `json:"role,omitempty"`
} }
type CreateVendorContactPayload struct { type CreateVendorContactPayload struct {
@@ -1135,10 +1136,10 @@ type EvidenceEdge struct {
} }
type ExportDocumentVersionPDFInput struct { type ExportDocumentVersionPDFInput struct {
DocumentVersionID gid.GID `json:"documentVersionId"` DocumentVersionID gid.GID `json:"documentVersionId"`
WithWatermark bool `json:"withWatermark"` WithWatermark bool `json:"withWatermark"`
WatermarkEmail *string `json:"watermarkEmail,omitempty"` WatermarkEmail *mail.Addr `json:"watermarkEmail,omitempty"`
WithSignatures bool `json:"withSignatures"` WithSignatures bool `json:"withSignatures"`
} }
type ExportDocumentVersionPDFPayload struct { type ExportDocumentVersionPDFPayload struct {
@@ -1254,7 +1255,7 @@ type InitiateDomainVerificationPayload struct {
type Invitation struct { type Invitation struct {
ID gid.GID `json:"id"` ID gid.GID `json:"id"`
Email string `json:"email"` Email mail.Addr `json:"email"`
FullName string `json:"fullName"` FullName string `json:"fullName"`
Role coredata.MembershipRole `json:"role"` Role coredata.MembershipRole `json:"role"`
Status coredata.InvitationStatus `json:"status"` Status coredata.InvitationStatus `json:"status"`
@@ -1283,7 +1284,7 @@ type InvitationOrder struct {
type InviteUserInput struct { type InviteUserInput struct {
OrganizationID gid.GID `json:"organizationId"` OrganizationID gid.GID `json:"organizationId"`
Email string `json:"email"` Email mail.Addr `json:"email"`
FullName string `json:"fullName"` FullName string `json:"fullName"`
Role coredata.MembershipRole `json:"role"` Role coredata.MembershipRole `json:"role"`
CreatePeople bool `json:"createPeople"` CreatePeople bool `json:"createPeople"`
@@ -1345,7 +1346,7 @@ type Membership struct {
OrganizationID gid.GID `json:"organizationID"` OrganizationID gid.GID `json:"organizationID"`
Role coredata.MembershipRole `json:"role"` Role coredata.MembershipRole `json:"role"`
FullName string `json:"fullName"` FullName string `json:"fullName"`
EmailAddress string `json:"emailAddress"` EmailAddress mail.Addr `json:"emailAddress"`
AuthMethod coredata.UserAuthMethod `json:"authMethod"` AuthMethod coredata.UserAuthMethod `json:"authMethod"`
CreatedAt time.Time `json:"createdAt"` CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"` UpdatedAt time.Time `json:"updatedAt"`
@@ -1493,8 +1494,8 @@ type PageInfo struct {
type People struct { type People struct {
ID gid.GID `json:"id"` ID gid.GID `json:"id"`
FullName string `json:"fullName"` FullName string `json:"fullName"`
PrimaryEmailAddress string `json:"primaryEmailAddress"` PrimaryEmailAddress mail.Addr `json:"primaryEmailAddress"`
AdditionalEmailAddresses []string `json:"additionalEmailAddresses"` AdditionalEmailAddresses []*mail.Addr `json:"additionalEmailAddresses"`
Kind coredata.PeopleKind `json:"kind"` Kind coredata.PeopleKind `json:"kind"`
Position *string `json:"position,omitempty"` Position *string `json:"position,omitempty"`
ContractStartDate *time.Time `json:"contractStartDate,omitempty"` ContractStartDate *time.Time `json:"contractStartDate,omitempty"`
@@ -1772,7 +1773,7 @@ func (this TrustCenter) GetID() gid.GID { return this.ID }
type TrustCenterAccess struct { type TrustCenterAccess struct {
ID gid.GID `json:"id"` ID gid.GID `json:"id"`
Email string `json:"email"` Email mail.Addr `json:"email"`
Name string `json:"name"` Name string `json:"name"`
Active bool `json:"active"` Active bool `json:"active"`
HasAcceptedNonDisclosureAgreement bool `json:"hasAcceptedNonDisclosureAgreement"` HasAcceptedNonDisclosureAgreement bool `json:"hasAcceptedNonDisclosureAgreement"`
@@ -2056,8 +2057,8 @@ type UpdateOrganizationPayload struct {
type UpdatePeopleInput struct { type UpdatePeopleInput struct {
ID gid.GID `json:"id"` ID gid.GID `json:"id"`
FullName *string `json:"fullName,omitempty"` FullName *string `json:"fullName,omitempty"`
PrimaryEmailAddress *string `json:"primaryEmailAddress,omitempty"` PrimaryEmailAddress *mail.Addr `json:"primaryEmailAddress,omitempty"`
AdditionalEmailAddresses []string `json:"additionalEmailAddresses,omitempty"` AdditionalEmailAddresses []*mail.Addr `json:"additionalEmailAddresses,omitempty"`
Kind *coredata.PeopleKind `json:"kind,omitempty"` Kind *coredata.PeopleKind `json:"kind,omitempty"`
Position graphql.Omittable[*string] `json:"position,omitempty"` Position graphql.Omittable[*string] `json:"position,omitempty"`
ContractStartDate graphql.Omittable[*time.Time] `json:"contractStartDate,omitempty"` ContractStartDate graphql.Omittable[*time.Time] `json:"contractStartDate,omitempty"`
@@ -2201,11 +2202,11 @@ type UpdateVendorBusinessAssociateAgreementPayload struct {
} }
type UpdateVendorContactInput struct { type UpdateVendorContactInput struct {
ID gid.GID `json:"id"` ID gid.GID `json:"id"`
FullName graphql.Omittable[*string] `json:"fullName,omitempty"` FullName graphql.Omittable[*string] `json:"fullName,omitempty"`
Email graphql.Omittable[*string] `json:"email,omitempty"` Email graphql.Omittable[*mail.Addr] `json:"email,omitempty"`
Phone graphql.Omittable[*string] `json:"phone,omitempty"` Phone graphql.Omittable[*string] `json:"phone,omitempty"`
Role graphql.Omittable[*string] `json:"role,omitempty"` Role graphql.Omittable[*string] `json:"role,omitempty"`
} }
type UpdateVendorContactPayload struct { type UpdateVendorContactPayload struct {
@@ -2329,7 +2330,7 @@ type UploadVendorDataPrivacyAgreementPayload struct {
type User struct { type User struct {
ID gid.GID `json:"id"` ID gid.GID `json:"id"`
FullName string `json:"fullName"` FullName string `json:"fullName"`
Email string `json:"email"` Email mail.Addr `json:"email"`
CreatedAt time.Time `json:"createdAt"` CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"` UpdatedAt time.Time `json:"updatedAt"`
} }
@@ -2419,14 +2420,14 @@ type VendorComplianceReportEdge struct {
} }
type VendorContact struct { type VendorContact struct {
ID gid.GID `json:"id"` ID gid.GID `json:"id"`
Vendor *Vendor `json:"vendor"` Vendor *Vendor `json:"vendor"`
FullName *string `json:"fullName,omitempty"` FullName *string `json:"fullName,omitempty"`
Email *string `json:"email,omitempty"` Email *mail.Addr `json:"email,omitempty"`
Phone *string `json:"phone,omitempty"` Phone *string `json:"phone,omitempty"`
Role *string `json:"role,omitempty"` Role *string `json:"role,omitempty"`
CreatedAt time.Time `json:"createdAt"` CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"` UpdatedAt time.Time `json:"updatedAt"`
} }
func (VendorContact) IsNode() {} func (VendorContact) IsNode() {}

View File

@@ -19,6 +19,7 @@ import (
"go.probo.inc/probo/pkg/authz" "go.probo.inc/probo/pkg/authz"
"go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/page"
"go.probo.inc/probo/pkg/probo" "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/schema"
@@ -1458,7 +1459,7 @@ func (r *mutationResolver) CreateOrganization(ctx context.Context, input types.C
OrganizationID: organization.ID, OrganizationID: organization.ID,
FullName: currentUser.FullName, FullName: currentUser.FullName,
PrimaryEmailAddress: currentUser.EmailAddress, PrimaryEmailAddress: currentUser.EmailAddress,
AdditionalEmailAddresses: []string{}, AdditionalEmailAddresses: []mail.Addr{},
Kind: coredata.PeopleKindEmployee, Kind: coredata.PeopleKindEmployee,
}, },
) )
@@ -1903,7 +1904,7 @@ func (r *mutationResolver) InviteUser(ctx context.Context, input types.InviteUse
OrganizationID: input.OrganizationID, OrganizationID: input.OrganizationID,
FullName: input.FullName, FullName: input.FullName,
PrimaryEmailAddress: input.Email, PrimaryEmailAddress: input.Email,
AdditionalEmailAddresses: []string{}, AdditionalEmailAddresses: []mail.Addr{},
Kind: coredata.PeopleKindEmployee, Kind: coredata.PeopleKindEmployee,
}) })
if err != nil { if err != nil {
@@ -1993,7 +1994,7 @@ func (r *mutationResolver) CreatePeople(ctx context.Context, input types.CreateP
OrganizationID: input.OrganizationID, OrganizationID: input.OrganizationID,
FullName: input.FullName, FullName: input.FullName,
PrimaryEmailAddress: input.PrimaryEmailAddress, PrimaryEmailAddress: input.PrimaryEmailAddress,
AdditionalEmailAddresses: []string{}, AdditionalEmailAddresses: []mail.Addr{},
Kind: input.Kind, Kind: input.Kind,
Position: input.Position, Position: input.Position,
ContractStartDate: input.ContractStartDate, ContractStartDate: input.ContractStartDate,
@@ -2019,11 +2020,15 @@ func (r *mutationResolver) UpdatePeople(ctx context.Context, input types.UpdateP
prb := r.ProboService(ctx, input.ID.TenantID()) prb := r.ProboService(ctx, input.ID.TenantID())
var emailAddresses []mail.Addr
for _, emailAddress := range input.AdditionalEmailAddresses {
emailAddresses = append(emailAddresses, *emailAddress)
}
people, err := prb.Peoples.Update(ctx, probo.UpdatePeopleRequest{ people, err := prb.Peoples.Update(ctx, probo.UpdatePeopleRequest{
ID: input.ID, ID: input.ID,
FullName: input.FullName, FullName: input.FullName,
PrimaryEmailAddress: input.PrimaryEmailAddress, PrimaryEmailAddress: input.PrimaryEmailAddress,
AdditionalEmailAddresses: &input.AdditionalEmailAddresses, AdditionalEmailAddresses: &emailAddresses,
Kind: input.Kind, Kind: input.Kind,
Position: UnwrapOmittable(input.Position), Position: UnwrapOmittable(input.Position),
ContractStartDate: UnwrapOmittable(input.ContractStartDate), ContractStartDate: UnwrapOmittable(input.ContractStartDate),

View File

@@ -5,6 +5,11 @@ info:
components: components:
schemas: schemas:
EmailAddress:
type: string
format: string
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/mail.Addr
Duration: Duration:
type: string type: string
description: A duration description: A duration
@@ -89,12 +94,12 @@ components:
type: string type: string
description: Full name description: Full name
primary_email_address: primary_email_address:
type: string $ref: "#/components/schemas/EmailAddress"
description: Primary email address description: Primary email address
additional_email_addresses: additional_email_addresses:
type: array type: array
items: items:
type: string $ref: "#/components/schemas/EmailAddress"
description: Additional email addresses description: Additional email addresses
kind: kind:
$ref: "#/components/schemas/PeopleKind" $ref: "#/components/schemas/PeopleKind"
@@ -372,12 +377,12 @@ components:
type: string type: string
description: Full name description: Full name
primary_email_address: primary_email_address:
type: string $ref: "#/components/schemas/EmailAddress"
description: Primary email address description: Primary email address
additional_email_addresses: additional_email_addresses:
type: array type: array
items: items:
type: string $ref: "#/components/schemas/EmailAddress"
description: Additional email addresses description: Additional email addresses
kind: kind:
$ref: "#/components/schemas/PeopleKind" $ref: "#/components/schemas/PeopleKind"
@@ -414,13 +419,13 @@ components:
type: string type: string
description: Full name description: Full name
primary_email_address: primary_email_address:
type: string $ref: "#/components/schemas/EmailAddress"
description: Primary email address description: Primary email address
additional_email_addresses: additional_email_addresses:
anyOf: anyOf:
- type: array - type: array
items: items:
type: string $ref: "#/components/schemas/EmailAddress"
- type: "null" - type: "null"
description: No additional email addresses description: No additional email addresses
description: Additional email addresses description: Additional email addresses

View File

@@ -6,6 +6,7 @@ import (
"go.probo.inc/mcpgen/mcp" "go.probo.inc/mcpgen/mcp"
"go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/page"
"time" "time"
) )
@@ -32,8 +33,8 @@ var (
AddNonconformityToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["nonconformity"],"properties":{"nonconformity":{"type":"object","required":["id","organization_id","reference_id","root_cause","owner_id","status","created_at","updated_at"],"properties":{"audit_id":{"description":"Audit ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No audit"}]},"corrective_action":{"description":"Corrective action"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"date_identified":{"description":"Date identified","format":"date-time"},"description":{"description":"Description"},"due_date":{"description":"Due date","format":"date-time"},"effectiveness_check":{"description":"Effectiveness check"},"id":{"type":"string","format":"string"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"reference_id":{"type":"string","description":"Reference ID"},"root_cause":{"type":"string","description":"Root cause"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"status":{"type":"string","enum":["OPEN","IN_PROGRESS","CLOSED"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`) AddNonconformityToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["nonconformity"],"properties":{"nonconformity":{"type":"object","required":["id","organization_id","reference_id","root_cause","owner_id","status","created_at","updated_at"],"properties":{"audit_id":{"description":"Audit ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No audit"}]},"corrective_action":{"description":"Corrective action"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"date_identified":{"description":"Date identified","format":"date-time"},"description":{"description":"Description"},"due_date":{"description":"Due date","format":"date-time"},"effectiveness_check":{"description":"Effectiveness check"},"id":{"type":"string","format":"string"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"reference_id":{"type":"string","description":"Reference ID"},"root_cause":{"type":"string","description":"Root cause"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"status":{"type":"string","enum":["OPEN","IN_PROGRESS","CLOSED"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`)
AddObligationToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["organization_id","owner_id","status"],"properties":{"actions_to_be_implemented":{"type":"string","description":"Actions to be implemented"},"area":{"type":"string","description":"Area"},"due_date":{"type":"string","description":"Due date","format":"date-time"},"last_review_date":{"type":"string","description":"Last review date","format":"date-time"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"regulator":{"type":"string","description":"Regulator"},"requirement":{"type":"string","description":"Requirement"},"source":{"type":"string","description":"Source"},"status":{"description":"Status","anyOf":[{"type":"string","enum":["NON_COMPLIANT","PARTIALLY_COMPLIANT","COMPLIANT"]},{"type":"null","description":"No status"}]}}}`) AddObligationToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["organization_id","owner_id","status"],"properties":{"actions_to_be_implemented":{"type":"string","description":"Actions to be implemented"},"area":{"type":"string","description":"Area"},"due_date":{"type":"string","description":"Due date","format":"date-time"},"last_review_date":{"type":"string","description":"Last review date","format":"date-time"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"regulator":{"type":"string","description":"Regulator"},"requirement":{"type":"string","description":"Requirement"},"source":{"type":"string","description":"Source"},"status":{"description":"Status","anyOf":[{"type":"string","enum":["NON_COMPLIANT","PARTIALLY_COMPLIANT","COMPLIANT"]},{"type":"null","description":"No status"}]}}}`)
AddObligationToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["obligation"],"properties":{"obligation":{"type":"object","required":["id","organization_id","owner_id","status","created_at","updated_at"],"properties":{"actions_to_be_implemented":{"description":"Actions to be implemented"},"area":{"description":"Area"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"due_date":{"description":"Due date","format":"date-time"},"id":{"type":"string","format":"string"},"last_review_date":{"description":"Last review date","format":"date-time"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"regulator":{"description":"Regulator"},"requirement":{"description":"Requirement"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"source":{"description":"Source"},"source_id":{"description":"Source ID"},"status":{"type":"string","enum":["NON_COMPLIANT","PARTIALLY_COMPLIANT","COMPLIANT"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`) AddObligationToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["obligation"],"properties":{"obligation":{"type":"object","required":["id","organization_id","owner_id","status","created_at","updated_at"],"properties":{"actions_to_be_implemented":{"description":"Actions to be implemented"},"area":{"description":"Area"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"due_date":{"description":"Due date","format":"date-time"},"id":{"type":"string","format":"string"},"last_review_date":{"description":"Last review date","format":"date-time"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"regulator":{"description":"Regulator"},"requirement":{"description":"Requirement"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"source":{"description":"Source"},"source_id":{"description":"Source ID"},"status":{"type":"string","enum":["NON_COMPLIANT","PARTIALLY_COMPLIANT","COMPLIANT"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`)
AddPeopleToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["organization_id","full_name","primary_email_address","kind"],"properties":{"additional_email_addresses":{"type":"array","description":"Additional email addresses","items":{"type":"string"}},"contract_end_date":{"type":"string","description":"Contract end date","format":"date-time"},"contract_start_date":{"type":"string","description":"Contract start date","format":"date-time"},"full_name":{"type":"string","description":"Full name"},"kind":{"type":"string","enum":["EMPLOYEE","CONTRACTOR","SERVICE_ACCOUNT"]},"organization_id":{"type":"string","format":"string"},"position":{"type":"string","description":"Position"},"primary_email_address":{"type":"string","description":"Primary email address"}}}`) AddPeopleToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["organization_id","full_name","primary_email_address","kind"],"properties":{"additional_email_addresses":{"type":"array","description":"Additional email addresses","items":{"type":"string","format":"string"}},"contract_end_date":{"type":"string","description":"Contract end date","format":"date-time"},"contract_start_date":{"type":"string","description":"Contract start date","format":"date-time"},"full_name":{"type":"string","description":"Full name"},"kind":{"type":"string","enum":["EMPLOYEE","CONTRACTOR","SERVICE_ACCOUNT"]},"organization_id":{"type":"string","format":"string"},"position":{"type":"string","description":"Position"},"primary_email_address":{"type":"string","format":"string"}}}`)
AddPeopleToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["people"],"properties":{"people":{"type":"object","required":["id","organization_id","full_name","primary_email_address","additional_email_addresses","kind","created_at","updated_at"],"properties":{"additional_email_addresses":{"type":"array","description":"Additional email addresses","items":{"type":"string"}},"contract_end_date":{"description":"Contract end date","format":"date-time"},"contract_start_date":{"description":"Contract start date","format":"date-time"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"full_name":{"type":"string","description":"Full name"},"id":{"type":"string","format":"string"},"kind":{"type":"string","enum":["EMPLOYEE","CONTRACTOR","SERVICE_ACCOUNT"]},"organization_id":{"type":"string","format":"string"},"position":{"description":"Position"},"primary_email_address":{"type":"string","description":"Primary email address"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`) AddPeopleToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["people"],"properties":{"people":{"type":"object","required":["id","organization_id","full_name","primary_email_address","additional_email_addresses","kind","created_at","updated_at"],"properties":{"additional_email_addresses":{"type":"array","description":"Additional email addresses","items":{"type":"string","format":"string"}},"contract_end_date":{"description":"Contract end date","format":"date-time"},"contract_start_date":{"description":"Contract start date","format":"date-time"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"full_name":{"type":"string","description":"Full name"},"id":{"type":"string","format":"string"},"kind":{"type":"string","enum":["EMPLOYEE","CONTRACTOR","SERVICE_ACCOUNT"]},"organization_id":{"type":"string","format":"string"},"position":{"description":"Position"},"primary_email_address":{"type":"string","format":"string"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`)
AddRiskToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["organization_id","name","category","treatment","inherent_likelihood","inherent_impact"],"properties":{"category":{"type":"string","description":"Risk category"},"description":{"type":"string","description":"Risk description"},"inherent_impact":{"type":"integer","description":"Inherent impact"},"inherent_likelihood":{"type":"integer","description":"Inherent likelihood"},"name":{"type":"string","description":"Risk name"},"note":{"type":"string","description":"Risk note"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"residual_impact":{"type":"integer","description":"Residual impact"},"residual_likelihood":{"type":"integer","description":"Residual likelihood"},"treatment":{"type":"string","enum":["MITIGATED","ACCEPTED","AVOIDED","TRANSFERRED"]}}}`) AddRiskToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["organization_id","name","category","treatment","inherent_likelihood","inherent_impact"],"properties":{"category":{"type":"string","description":"Risk category"},"description":{"type":"string","description":"Risk description"},"inherent_impact":{"type":"integer","description":"Inherent impact"},"inherent_likelihood":{"type":"integer","description":"Inherent likelihood"},"name":{"type":"string","description":"Risk name"},"note":{"type":"string","description":"Risk note"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"residual_impact":{"type":"integer","description":"Residual impact"},"residual_likelihood":{"type":"integer","description":"Residual likelihood"},"treatment":{"type":"string","enum":["MITIGATED","ACCEPTED","AVOIDED","TRANSFERRED"]}}}`)
AddRiskToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["risk"],"properties":{"risk":{"type":"object","required":["id","organization_id","name","category","treatment","inherent_likelihood","inherent_impact","inherent_risk_score","residual_likelihood","residual_impact","residual_risk_score","note","created_at","updated_at"],"properties":{"category":{"type":"string","description":"Risk category"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Risk description"},"id":{"type":"string","format":"string"},"inherent_impact":{"type":"integer","description":"Inherent impact"},"inherent_likelihood":{"type":"integer","description":"Inherent likelihood"},"inherent_risk_score":{"type":"integer","description":"Inherent risk score"},"name":{"type":"string","description":"Risk name"},"note":{"type":"string","description":"Risk note"},"organization_id":{"type":"string","format":"string"},"owner_id":{"anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No owner"}]},"residual_impact":{"type":"integer","description":"Residual impact"},"residual_likelihood":{"type":"integer","description":"Residual likelihood"},"residual_risk_score":{"type":"integer","description":"Residual risk score"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"treatment":{"type":"string","enum":["MITIGATED","ACCEPTED","AVOIDED","TRANSFERRED"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`) AddRiskToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["risk"],"properties":{"risk":{"type":"object","required":["id","organization_id","name","category","treatment","inherent_likelihood","inherent_impact","inherent_risk_score","residual_likelihood","residual_impact","residual_risk_score","note","created_at","updated_at"],"properties":{"category":{"type":"string","description":"Risk category"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Risk description"},"id":{"type":"string","format":"string"},"inherent_impact":{"type":"integer","description":"Inherent impact"},"inherent_likelihood":{"type":"integer","description":"Inherent likelihood"},"inherent_risk_score":{"type":"integer","description":"Inherent risk score"},"name":{"type":"string","description":"Risk name"},"note":{"type":"string","description":"Risk note"},"organization_id":{"type":"string","format":"string"},"owner_id":{"anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No owner"}]},"residual_impact":{"type":"integer","description":"Residual impact"},"residual_likelihood":{"type":"integer","description":"Residual likelihood"},"residual_risk_score":{"type":"integer","description":"Residual risk score"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"treatment":{"type":"string","enum":["MITIGATED","ACCEPTED","AVOIDED","TRANSFERRED"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`)
AddTaskToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["organization_id","name"],"properties":{"assigned_to_id":{"description":"Assigned to person ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"Not assigned"}]},"deadline":{"type":"string","description":"Deadline","format":"date-time"},"description":{"type":"string","description":"Task description"},"measure_id":{"description":"Measure ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No measure"}]},"name":{"type":"string","description":"Task name"},"organization_id":{"type":"string","format":"string"},"time_estimate":{"type":"string","description":"A duration"}}}`) AddTaskToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["organization_id","name"],"properties":{"assigned_to_id":{"description":"Assigned to person ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"Not assigned"}]},"deadline":{"type":"string","description":"Deadline","format":"date-time"},"description":{"type":"string","description":"Task description"},"measure_id":{"description":"Measure ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No measure"}]},"name":{"type":"string","description":"Task name"},"organization_id":{"type":"string","format":"string"},"time_estimate":{"type":"string","description":"A duration"}}}`)
@@ -75,7 +76,7 @@ var (
GetObligationToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["id"],"properties":{"id":{"type":"string","format":"string"}}}`) GetObligationToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["id"],"properties":{"id":{"type":"string","format":"string"}}}`)
GetObligationToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["obligation"],"properties":{"obligation":{"type":"object","required":["id","organization_id","owner_id","status","created_at","updated_at"],"properties":{"actions_to_be_implemented":{"description":"Actions to be implemented"},"area":{"description":"Area"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"due_date":{"description":"Due date","format":"date-time"},"id":{"type":"string","format":"string"},"last_review_date":{"description":"Last review date","format":"date-time"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"regulator":{"description":"Regulator"},"requirement":{"description":"Requirement"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"source":{"description":"Source"},"source_id":{"description":"Source ID"},"status":{"type":"string","enum":["NON_COMPLIANT","PARTIALLY_COMPLIANT","COMPLIANT"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`) GetObligationToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["obligation"],"properties":{"obligation":{"type":"object","required":["id","organization_id","owner_id","status","created_at","updated_at"],"properties":{"actions_to_be_implemented":{"description":"Actions to be implemented"},"area":{"description":"Area"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"due_date":{"description":"Due date","format":"date-time"},"id":{"type":"string","format":"string"},"last_review_date":{"description":"Last review date","format":"date-time"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"regulator":{"description":"Regulator"},"requirement":{"description":"Requirement"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"source":{"description":"Source"},"source_id":{"description":"Source ID"},"status":{"type":"string","enum":["NON_COMPLIANT","PARTIALLY_COMPLIANT","COMPLIANT"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`)
GetPeopleToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["id"],"properties":{"id":{"type":"string","format":"string"}}}`) GetPeopleToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["id"],"properties":{"id":{"type":"string","format":"string"}}}`)
GetPeopleToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["people"],"properties":{"people":{"type":"object","required":["id","organization_id","full_name","primary_email_address","additional_email_addresses","kind","created_at","updated_at"],"properties":{"additional_email_addresses":{"type":"array","description":"Additional email addresses","items":{"type":"string"}},"contract_end_date":{"description":"Contract end date","format":"date-time"},"contract_start_date":{"description":"Contract start date","format":"date-time"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"full_name":{"type":"string","description":"Full name"},"id":{"type":"string","format":"string"},"kind":{"type":"string","enum":["EMPLOYEE","CONTRACTOR","SERVICE_ACCOUNT"]},"organization_id":{"type":"string","format":"string"},"position":{"description":"Position"},"primary_email_address":{"type":"string","description":"Primary email address"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`) GetPeopleToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["people"],"properties":{"people":{"type":"object","required":["id","organization_id","full_name","primary_email_address","additional_email_addresses","kind","created_at","updated_at"],"properties":{"additional_email_addresses":{"type":"array","description":"Additional email addresses","items":{"type":"string","format":"string"}},"contract_end_date":{"description":"Contract end date","format":"date-time"},"contract_start_date":{"description":"Contract start date","format":"date-time"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"full_name":{"type":"string","description":"Full name"},"id":{"type":"string","format":"string"},"kind":{"type":"string","enum":["EMPLOYEE","CONTRACTOR","SERVICE_ACCOUNT"]},"organization_id":{"type":"string","format":"string"},"position":{"description":"Position"},"primary_email_address":{"type":"string","format":"string"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`)
GetRiskToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["id"],"properties":{"id":{"type":"string","format":"string"}}}`) GetRiskToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["id"],"properties":{"id":{"type":"string","format":"string"}}}`)
GetRiskToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["risk"],"properties":{"risk":{"type":"object","required":["id","organization_id","name","category","treatment","inherent_likelihood","inherent_impact","inherent_risk_score","residual_likelihood","residual_impact","residual_risk_score","note","created_at","updated_at"],"properties":{"category":{"type":"string","description":"Risk category"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Risk description"},"id":{"type":"string","format":"string"},"inherent_impact":{"type":"integer","description":"Inherent impact"},"inherent_likelihood":{"type":"integer","description":"Inherent likelihood"},"inherent_risk_score":{"type":"integer","description":"Inherent risk score"},"name":{"type":"string","description":"Risk name"},"note":{"type":"string","description":"Risk note"},"organization_id":{"type":"string","format":"string"},"owner_id":{"anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No owner"}]},"residual_impact":{"type":"integer","description":"Residual impact"},"residual_likelihood":{"type":"integer","description":"Residual likelihood"},"residual_risk_score":{"type":"integer","description":"Residual risk score"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"treatment":{"type":"string","enum":["MITIGATED","ACCEPTED","AVOIDED","TRANSFERRED"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`) GetRiskToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["risk"],"properties":{"risk":{"type":"object","required":["id","organization_id","name","category","treatment","inherent_likelihood","inherent_impact","inherent_risk_score","residual_likelihood","residual_impact","residual_risk_score","note","created_at","updated_at"],"properties":{"category":{"type":"string","description":"Risk category"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Risk description"},"id":{"type":"string","format":"string"},"inherent_impact":{"type":"integer","description":"Inherent impact"},"inherent_likelihood":{"type":"integer","description":"Inherent likelihood"},"inherent_risk_score":{"type":"integer","description":"Inherent risk score"},"name":{"type":"string","description":"Risk name"},"note":{"type":"string","description":"Risk note"},"organization_id":{"type":"string","format":"string"},"owner_id":{"anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No owner"}]},"residual_impact":{"type":"integer","description":"Residual impact"},"residual_likelihood":{"type":"integer","description":"Residual likelihood"},"residual_risk_score":{"type":"integer","description":"Residual risk score"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"treatment":{"type":"string","enum":["MITIGATED","ACCEPTED","AVOIDED","TRANSFERRED"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`)
GetSnapshotToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["id"],"properties":{"id":{"type":"string","format":"string"}}}`) GetSnapshotToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["id"],"properties":{"id":{"type":"string","format":"string"}}}`)
@@ -117,7 +118,7 @@ var (
ListOrganizationsToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"organization_id":{"type":"string","format":"string"}}}`) ListOrganizationsToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"organization_id":{"type":"string","format":"string"}}}`)
ListOrganizationsToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"organizations":{"type":"array","items":{"type":"object","required":["id","name","description","created_at","updated_at"],"properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Organization description"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Organization name"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}}`) ListOrganizationsToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"organizations":{"type":"array","items":{"type":"object","required":["id","name","description","created_at","updated_at"],"properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Organization description"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Organization name"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}}`)
ListPeopleToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["organization_id"],"properties":{"cursor":{"type":"string","format":"string"},"filter":{"type":"object","properties":{"exclude_contract_ended":{"type":"boolean","description":"Exclude people with ended contracts"}}},"order_by":{"type":"object","required":["field","direction"],"properties":{"direction":{"type":"string","enum":["ASC","DESC"]},"field":{"type":"string","enum":["CREATED_AT","FULL_NAME","KIND"]}}},"organization_id":{"type":"string","format":"string"},"size":{"type":"integer","description":"Page size"}}}`) ListPeopleToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["organization_id"],"properties":{"cursor":{"type":"string","format":"string"},"filter":{"type":"object","properties":{"exclude_contract_ended":{"type":"boolean","description":"Exclude people with ended contracts"}}},"order_by":{"type":"object","required":["field","direction"],"properties":{"direction":{"type":"string","enum":["ASC","DESC"]},"field":{"type":"string","enum":["CREATED_AT","FULL_NAME","KIND"]}}},"organization_id":{"type":"string","format":"string"},"size":{"type":"integer","description":"Page size"}}}`)
ListPeopleToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["people"],"properties":{"next_cursor":{"type":"string","format":"string"},"people":{"type":"array","items":{"type":"object","required":["id","organization_id","full_name","primary_email_address","additional_email_addresses","kind","created_at","updated_at"],"properties":{"additional_email_addresses":{"type":"array","description":"Additional email addresses","items":{"type":"string"}},"contract_end_date":{"description":"Contract end date","format":"date-time"},"contract_start_date":{"description":"Contract start date","format":"date-time"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"full_name":{"type":"string","description":"Full name"},"id":{"type":"string","format":"string"},"kind":{"type":"string","enum":["EMPLOYEE","CONTRACTOR","SERVICE_ACCOUNT"]},"organization_id":{"type":"string","format":"string"},"position":{"description":"Position"},"primary_email_address":{"type":"string","description":"Primary email address"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}}`) ListPeopleToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["people"],"properties":{"next_cursor":{"type":"string","format":"string"},"people":{"type":"array","items":{"type":"object","required":["id","organization_id","full_name","primary_email_address","additional_email_addresses","kind","created_at","updated_at"],"properties":{"additional_email_addresses":{"type":"array","description":"Additional email addresses","items":{"type":"string","format":"string"}},"contract_end_date":{"description":"Contract end date","format":"date-time"},"contract_start_date":{"description":"Contract start date","format":"date-time"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"full_name":{"type":"string","description":"Full name"},"id":{"type":"string","format":"string"},"kind":{"type":"string","enum":["EMPLOYEE","CONTRACTOR","SERVICE_ACCOUNT"]},"organization_id":{"type":"string","format":"string"},"position":{"description":"Position"},"primary_email_address":{"type":"string","format":"string"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}}`)
ListRisksToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["organization_id"],"properties":{"cursor":{"type":"string","format":"string"},"filter":{"type":"object","properties":{"query":{"type":"string","description":"Search query"},"snapshot_id":{"type":"string","format":"string"}}},"order_by":{"type":"object","required":["field","direction"],"properties":{"direction":{"type":"string","enum":["ASC","DESC"]},"field":{"type":"string","enum":["CREATED_AT","UPDATED_AT","NAME","CATEGORY","TREATMENT","INHERENT_RISK_SCORE","RESIDUAL_RISK_SCORE","OWNER_FULL_NAME"]}}},"organization_id":{"type":"string","format":"string"},"size":{"type":"integer","description":"Page size"}}}`) ListRisksToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["organization_id"],"properties":{"cursor":{"type":"string","format":"string"},"filter":{"type":"object","properties":{"query":{"type":"string","description":"Search query"},"snapshot_id":{"type":"string","format":"string"}}},"order_by":{"type":"object","required":["field","direction"],"properties":{"direction":{"type":"string","enum":["ASC","DESC"]},"field":{"type":"string","enum":["CREATED_AT","UPDATED_AT","NAME","CATEGORY","TREATMENT","INHERENT_RISK_SCORE","RESIDUAL_RISK_SCORE","OWNER_FULL_NAME"]}}},"organization_id":{"type":"string","format":"string"},"size":{"type":"integer","description":"Page size"}}}`)
ListRisksToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["risks"],"properties":{"next_cursor":{"type":"string","format":"string"},"risks":{"type":"array","items":{"type":"object","required":["id","organization_id","name","category","treatment","inherent_likelihood","inherent_impact","inherent_risk_score","residual_likelihood","residual_impact","residual_risk_score","note","created_at","updated_at"],"properties":{"category":{"type":"string","description":"Risk category"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Risk description"},"id":{"type":"string","format":"string"},"inherent_impact":{"type":"integer","description":"Inherent impact"},"inherent_likelihood":{"type":"integer","description":"Inherent likelihood"},"inherent_risk_score":{"type":"integer","description":"Inherent risk score"},"name":{"type":"string","description":"Risk name"},"note":{"type":"string","description":"Risk note"},"organization_id":{"type":"string","format":"string"},"owner_id":{"anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No owner"}]},"residual_impact":{"type":"integer","description":"Residual impact"},"residual_likelihood":{"type":"integer","description":"Residual likelihood"},"residual_risk_score":{"type":"integer","description":"Residual risk score"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"treatment":{"type":"string","enum":["MITIGATED","ACCEPTED","AVOIDED","TRANSFERRED"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}}`) ListRisksToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["risks"],"properties":{"next_cursor":{"type":"string","format":"string"},"risks":{"type":"array","items":{"type":"object","required":["id","organization_id","name","category","treatment","inherent_likelihood","inherent_impact","inherent_risk_score","residual_likelihood","residual_impact","residual_risk_score","note","created_at","updated_at"],"properties":{"category":{"type":"string","description":"Risk category"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Risk description"},"id":{"type":"string","format":"string"},"inherent_impact":{"type":"integer","description":"Inherent impact"},"inherent_likelihood":{"type":"integer","description":"Inherent likelihood"},"inherent_risk_score":{"type":"integer","description":"Inherent risk score"},"name":{"type":"string","description":"Risk name"},"note":{"type":"string","description":"Risk note"},"organization_id":{"type":"string","format":"string"},"owner_id":{"anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No owner"}]},"residual_impact":{"type":"integer","description":"Residual impact"},"residual_likelihood":{"type":"integer","description":"Residual likelihood"},"residual_risk_score":{"type":"integer","description":"Residual risk score"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"treatment":{"type":"string","enum":["MITIGATED","ACCEPTED","AVOIDED","TRANSFERRED"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}}`)
ListSnapshotsToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["organization_id"],"properties":{"cursor":{"type":"string","format":"string"},"order_by":{"type":"object","required":["field","direction"],"properties":{"direction":{"type":"string","enum":["ASC","DESC"]},"field":{"type":"string","enum":["CREATED_AT","NAME","TYPE"]}}},"organization_id":{"type":"string","format":"string"},"size":{"type":"integer","description":"Page size"}}}`) ListSnapshotsToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["organization_id"],"properties":{"cursor":{"type":"string","format":"string"},"order_by":{"type":"object","required":["field","direction"],"properties":{"direction":{"type":"string","enum":["ASC","DESC"]},"field":{"type":"string","enum":["CREATED_AT","NAME","TYPE"]}}},"organization_id":{"type":"string","format":"string"},"size":{"type":"integer","description":"Page size"}}}`)
@@ -164,8 +165,8 @@ var (
UpdateNonconformityToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["nonconformity"],"properties":{"nonconformity":{"type":"object","required":["id","organization_id","reference_id","root_cause","owner_id","status","created_at","updated_at"],"properties":{"audit_id":{"description":"Audit ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No audit"}]},"corrective_action":{"description":"Corrective action"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"date_identified":{"description":"Date identified","format":"date-time"},"description":{"description":"Description"},"due_date":{"description":"Due date","format":"date-time"},"effectiveness_check":{"description":"Effectiveness check"},"id":{"type":"string","format":"string"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"reference_id":{"type":"string","description":"Reference ID"},"root_cause":{"type":"string","description":"Root cause"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"status":{"type":"string","enum":["OPEN","IN_PROGRESS","CLOSED"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`) UpdateNonconformityToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["nonconformity"],"properties":{"nonconformity":{"type":"object","required":["id","organization_id","reference_id","root_cause","owner_id","status","created_at","updated_at"],"properties":{"audit_id":{"description":"Audit ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No audit"}]},"corrective_action":{"description":"Corrective action"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"date_identified":{"description":"Date identified","format":"date-time"},"description":{"description":"Description"},"due_date":{"description":"Due date","format":"date-time"},"effectiveness_check":{"description":"Effectiveness check"},"id":{"type":"string","format":"string"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"reference_id":{"type":"string","description":"Reference ID"},"root_cause":{"type":"string","description":"Root cause"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"status":{"type":"string","enum":["OPEN","IN_PROGRESS","CLOSED"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`)
UpdateObligationToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["id"],"properties":{"actions_to_be_implemented":{"description":"Actions to be implemented"},"area":{"description":"Area"},"due_date":{"description":"Due date","format":"date-time"},"id":{"type":"string","format":"string"},"last_review_date":{"description":"Last review date","format":"date-time"},"owner_id":{"description":"Owner ID","anyOf":[{"type":"string","format":"string"},{"type":"null"}]},"regulator":{"description":"Regulator"},"requirement":{"description":"Requirement"},"source":{"description":"Source"},"status":{"description":"Status","anyOf":[{"type":"string","enum":["NON_COMPLIANT","PARTIALLY_COMPLIANT","COMPLIANT"]},{"type":"null","description":"No status"}]}}}`) UpdateObligationToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["id"],"properties":{"actions_to_be_implemented":{"description":"Actions to be implemented"},"area":{"description":"Area"},"due_date":{"description":"Due date","format":"date-time"},"id":{"type":"string","format":"string"},"last_review_date":{"description":"Last review date","format":"date-time"},"owner_id":{"description":"Owner ID","anyOf":[{"type":"string","format":"string"},{"type":"null"}]},"regulator":{"description":"Regulator"},"requirement":{"description":"Requirement"},"source":{"description":"Source"},"status":{"description":"Status","anyOf":[{"type":"string","enum":["NON_COMPLIANT","PARTIALLY_COMPLIANT","COMPLIANT"]},{"type":"null","description":"No status"}]}}}`)
UpdateObligationToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["obligation"],"properties":{"obligation":{"type":"object","required":["id","organization_id","owner_id","status","created_at","updated_at"],"properties":{"actions_to_be_implemented":{"description":"Actions to be implemented"},"area":{"description":"Area"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"due_date":{"description":"Due date","format":"date-time"},"id":{"type":"string","format":"string"},"last_review_date":{"description":"Last review date","format":"date-time"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"regulator":{"description":"Regulator"},"requirement":{"description":"Requirement"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"source":{"description":"Source"},"source_id":{"description":"Source ID"},"status":{"type":"string","enum":["NON_COMPLIANT","PARTIALLY_COMPLIANT","COMPLIANT"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`) UpdateObligationToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["obligation"],"properties":{"obligation":{"type":"object","required":["id","organization_id","owner_id","status","created_at","updated_at"],"properties":{"actions_to_be_implemented":{"description":"Actions to be implemented"},"area":{"description":"Area"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"due_date":{"description":"Due date","format":"date-time"},"id":{"type":"string","format":"string"},"last_review_date":{"description":"Last review date","format":"date-time"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"regulator":{"description":"Regulator"},"requirement":{"description":"Requirement"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"source":{"description":"Source"},"source_id":{"description":"Source ID"},"status":{"type":"string","enum":["NON_COMPLIANT","PARTIALLY_COMPLIANT","COMPLIANT"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`)
UpdatePeopleToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["id"],"properties":{"additional_email_addresses":{"description":"Additional email addresses","anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null","description":"No additional email addresses"}]},"contract_end_date":{"description":"Contract end date","format":"date-time"},"contract_start_date":{"description":"Contract start date","format":"date-time"},"full_name":{"type":"string","description":"Full name"},"id":{"type":"string","format":"string"},"kind":{"type":"string","enum":["EMPLOYEE","CONTRACTOR","SERVICE_ACCOUNT"]},"position":{"description":"Position"},"primary_email_address":{"type":"string","description":"Primary email address"}}}`) UpdatePeopleToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["id"],"properties":{"additional_email_addresses":{"description":"Additional email addresses","anyOf":[{"type":"array","items":{"type":"string","format":"string"}},{"type":"null","description":"No additional email addresses"}]},"contract_end_date":{"description":"Contract end date","format":"date-time"},"contract_start_date":{"description":"Contract start date","format":"date-time"},"full_name":{"type":"string","description":"Full name"},"id":{"type":"string","format":"string"},"kind":{"type":"string","enum":["EMPLOYEE","CONTRACTOR","SERVICE_ACCOUNT"]},"position":{"description":"Position"},"primary_email_address":{"type":"string","format":"string"}}}`)
UpdatePeopleToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["people"],"properties":{"people":{"type":"object","required":["id","organization_id","full_name","primary_email_address","additional_email_addresses","kind","created_at","updated_at"],"properties":{"additional_email_addresses":{"type":"array","description":"Additional email addresses","items":{"type":"string"}},"contract_end_date":{"description":"Contract end date","format":"date-time"},"contract_start_date":{"description":"Contract start date","format":"date-time"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"full_name":{"type":"string","description":"Full name"},"id":{"type":"string","format":"string"},"kind":{"type":"string","enum":["EMPLOYEE","CONTRACTOR","SERVICE_ACCOUNT"]},"organization_id":{"type":"string","format":"string"},"position":{"description":"Position"},"primary_email_address":{"type":"string","description":"Primary email address"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`) UpdatePeopleToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["people"],"properties":{"people":{"type":"object","required":["id","organization_id","full_name","primary_email_address","additional_email_addresses","kind","created_at","updated_at"],"properties":{"additional_email_addresses":{"type":"array","description":"Additional email addresses","items":{"type":"string","format":"string"}},"contract_end_date":{"description":"Contract end date","format":"date-time"},"contract_start_date":{"description":"Contract start date","format":"date-time"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"full_name":{"type":"string","description":"Full name"},"id":{"type":"string","format":"string"},"kind":{"type":"string","enum":["EMPLOYEE","CONTRACTOR","SERVICE_ACCOUNT"]},"organization_id":{"type":"string","format":"string"},"position":{"description":"Position"},"primary_email_address":{"type":"string","format":"string"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`)
UpdateRiskToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["id"],"properties":{"category":{"type":"string","description":"Risk category"},"description":{"description":"Risk description"},"id":{"type":"string","format":"string"},"inherent_impact":{"type":"integer","description":"Inherent impact"},"inherent_likelihood":{"type":"integer","description":"Inherent likelihood"},"name":{"type":"string","description":"Risk name"},"note":{"type":"string","description":"Risk note"},"owner_id":{"description":"Owner ID","anyOf":[{"type":"string","format":"string"},{"type":"null"}]},"residual_impact":{"type":"integer","description":"Residual impact"},"residual_likelihood":{"type":"integer","description":"Residual likelihood"},"treatment":{"type":"string","enum":["MITIGATED","ACCEPTED","AVOIDED","TRANSFERRED"]}}}`) UpdateRiskToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["id"],"properties":{"category":{"type":"string","description":"Risk category"},"description":{"description":"Risk description"},"id":{"type":"string","format":"string"},"inherent_impact":{"type":"integer","description":"Inherent impact"},"inherent_likelihood":{"type":"integer","description":"Inherent likelihood"},"name":{"type":"string","description":"Risk name"},"note":{"type":"string","description":"Risk note"},"owner_id":{"description":"Owner ID","anyOf":[{"type":"string","format":"string"},{"type":"null"}]},"residual_impact":{"type":"integer","description":"Residual impact"},"residual_likelihood":{"type":"integer","description":"Residual likelihood"},"treatment":{"type":"string","enum":["MITIGATED","ACCEPTED","AVOIDED","TRANSFERRED"]}}}`)
UpdateRiskToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["risk"],"properties":{"risk":{"type":"object","required":["id","organization_id","name","category","treatment","inherent_likelihood","inherent_impact","inherent_risk_score","residual_likelihood","residual_impact","residual_risk_score","note","created_at","updated_at"],"properties":{"category":{"type":"string","description":"Risk category"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Risk description"},"id":{"type":"string","format":"string"},"inherent_impact":{"type":"integer","description":"Inherent impact"},"inherent_likelihood":{"type":"integer","description":"Inherent likelihood"},"inherent_risk_score":{"type":"integer","description":"Inherent risk score"},"name":{"type":"string","description":"Risk name"},"note":{"type":"string","description":"Risk note"},"organization_id":{"type":"string","format":"string"},"owner_id":{"anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No owner"}]},"residual_impact":{"type":"integer","description":"Residual impact"},"residual_likelihood":{"type":"integer","description":"Residual likelihood"},"residual_risk_score":{"type":"integer","description":"Residual risk score"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"treatment":{"type":"string","enum":["MITIGATED","ACCEPTED","AVOIDED","TRANSFERRED"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`) UpdateRiskToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["risk"],"properties":{"risk":{"type":"object","required":["id","organization_id","name","category","treatment","inherent_likelihood","inherent_impact","inherent_risk_score","residual_likelihood","residual_impact","residual_risk_score","note","created_at","updated_at"],"properties":{"category":{"type":"string","description":"Risk category"},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Risk description"},"id":{"type":"string","format":"string"},"inherent_impact":{"type":"integer","description":"Inherent impact"},"inherent_likelihood":{"type":"integer","description":"Inherent likelihood"},"inherent_risk_score":{"type":"integer","description":"Inherent risk score"},"name":{"type":"string","description":"Risk name"},"note":{"type":"string","description":"Risk note"},"organization_id":{"type":"string","format":"string"},"owner_id":{"anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No owner"}]},"residual_impact":{"type":"integer","description":"Residual impact"},"residual_likelihood":{"type":"integer","description":"Residual likelihood"},"residual_risk_score":{"type":"integer","description":"Residual risk score"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"treatment":{"type":"string","enum":["MITIGATED","ACCEPTED","AVOIDED","TRANSFERRED"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}}}}}`)
UpdateTaskToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["id"],"properties":{"deadline":{"description":"Deadline","anyOf":[{"type":"string","description":"Deadline","format":"date-time"},{"type":"null","description":"No deadline"}]},"description":{"description":"Task description"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Task name"},"state":{"description":"Task state","anyOf":[{"type":"string","enum":["TODO","DONE"]},{"type":"null","description":"No state"}]},"time_estimate":{"description":"Time estimate","anyOf":[{"type":"string","description":"A duration"},{"type":"null","description":"No time estimate"}]}}}`) UpdateTaskToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","required":["id"],"properties":{"deadline":{"description":"Deadline","anyOf":[{"type":"string","description":"Deadline","format":"date-time"},{"type":"null","description":"No deadline"}]},"description":{"description":"Task description"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Task name"},"state":{"description":"Task state","anyOf":[{"type":"string","enum":["TODO","DONE"]},{"type":"null","description":"No state"}]},"time_estimate":{"description":"Time estimate","anyOf":[{"type":"string","description":"A duration"},{"type":"null","description":"No time estimate"}]}}}`)
@@ -404,7 +405,7 @@ type AddObligationOutput struct {
// AddPeopleInput represents the schema // AddPeopleInput represents the schema
type AddPeopleInput struct { type AddPeopleInput struct {
// Additional email addresses // Additional email addresses
AdditionalEmailAddresses []string `json:"additional_email_addresses,omitempty"` AdditionalEmailAddresses []mail.Addr `json:"additional_email_addresses,omitempty"`
// Contract end date // Contract end date
ContractEndDate *time.Time `json:"contract_end_date,omitempty"` ContractEndDate *time.Time `json:"contract_end_date,omitempty"`
// Contract start date // Contract start date
@@ -418,7 +419,7 @@ type AddPeopleInput struct {
// Position // Position
Position *string `json:"position,omitempty"` Position *string `json:"position,omitempty"`
// Primary email address // Primary email address
PrimaryEmailAddress string `json:"primary_email_address"` PrimaryEmailAddress mail.Addr `json:"primary_email_address"`
} }
// AddPeopleOutput represents the schema // AddPeopleOutput represents the schema
@@ -1548,7 +1549,7 @@ type Organization struct {
// People represents the schema // People represents the schema
type People struct { type People struct {
// Additional email addresses // Additional email addresses
AdditionalEmailAddresses []string `json:"additional_email_addresses"` AdditionalEmailAddresses []mail.Addr `json:"additional_email_addresses"`
// Contract end date // Contract end date
ContractEndDate *time.Time `json:"contract_end_date,omitempty"` ContractEndDate *time.Time `json:"contract_end_date,omitempty"`
// Contract start date // Contract start date
@@ -1566,7 +1567,7 @@ type People struct {
// Position // Position
Position *string `json:"position,omitempty"` Position *string `json:"position,omitempty"`
// Primary email address // Primary email address
PrimaryEmailAddress string `json:"primary_email_address"` PrimaryEmailAddress mail.Addr `json:"primary_email_address"`
// Update timestamp // Update timestamp
UpdatedAt time.Time `json:"updated_at"` UpdatedAt time.Time `json:"updated_at"`
} }
@@ -2025,7 +2026,7 @@ type UpdateObligationOutput struct {
// UpdatePeopleInput represents the schema // UpdatePeopleInput represents the schema
type UpdatePeopleInput struct { type UpdatePeopleInput struct {
// Additional email addresses // Additional email addresses
AdditionalEmailAddresses *[]string `json:"additional_email_addresses,omitempty"` AdditionalEmailAddresses *[]mail.Addr `json:"additional_email_addresses,omitempty"`
// Contract end date // Contract end date
ContractEndDate mcp.Omittable[*time.Time] `json:"contract_end_date,omitempty"` ContractEndDate mcp.Omittable[*time.Time] `json:"contract_end_date,omitempty"`
// Contract start date // Contract start date
@@ -2039,7 +2040,7 @@ type UpdatePeopleInput struct {
// Position // Position
Position mcp.Omittable[*string] `json:"position,omitempty"` Position mcp.Omittable[*string] `json:"position,omitempty"`
// Primary email address // Primary email address
PrimaryEmailAddress *string `json:"primary_email_address,omitempty"` PrimaryEmailAddress *mail.Addr `json:"primary_email_address,omitempty"`
} }
// UpdatePeopleOutput represents the schema // UpdatePeopleOutput represents the schema

View File

@@ -146,7 +146,7 @@ func SlackHandler(slackSvc *slack.Service, slackSigningSecret string, logger *lo
return return
} }
if initialSlackMessage.RequesterEmail == nil || *initialSlackMessage.RequesterEmail == "" { if initialSlackMessage.RequesterEmail == nil {
logger.ErrorCtx(ctx, "missing requester email", log.String("slack_message_id", initialSlackMessage.ID.String())) logger.ErrorCtx(ctx, "missing requester email", log.String("slack_message_id", initialSlackMessage.ID.String()))
httpserver.RenderJSON(w, http.StatusInternalServerError, SlackInteractiveResponse{Success: false, Message: "internal server error"}) httpserver.RenderJSON(w, http.StatusInternalServerError, SlackInteractiveResponse{Success: false, Message: "internal server error"})
return return

View File

@@ -27,3 +27,6 @@ models:
CursorKey: CursorKey:
model: model:
- "go.probo.inc/probo/pkg/server/gqlutils/types/cursor.CursorKeyScalar" - "go.probo.inc/probo/pkg/server/gqlutils/types/cursor.CursorKeyScalar"
EmailAddr:
model:
- "go.probo.inc/probo/pkg/server/gqlutils/types/mail.AddrScalar"

View File

@@ -19,8 +19,9 @@ enum Role {
USER USER
} }
scalar Datetime
scalar CursorKey scalar CursorKey
scalar Datetime
scalar EmailAddr
interface Node { interface Node {
id: ID! id: ID!
@@ -46,11 +47,9 @@ type Organization implements Node {
enum DocumentType enum DocumentType
@goModel(model: "go.probo.inc/probo/pkg/coredata.DocumentType") { @goModel(model: "go.probo.inc/probo/pkg/coredata.DocumentType") {
OTHER OTHER @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeOther")
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeOther")
ISMS @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeISMS") ISMS @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeISMS")
POLICY POLICY @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypePolicy")
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypePolicy")
PROCEDURE PROCEDURE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeProcedure") @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeProcedure")
} }
@@ -357,9 +356,7 @@ enum CountryCode
enum VendorCategory enum VendorCategory
@goModel(model: "go.probo.inc/probo/pkg/coredata.VendorCategory") { @goModel(model: "go.probo.inc/probo/pkg/coredata.VendorCategory") {
ANALYTICS ANALYTICS
@goEnum( @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryAnalytics")
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryAnalytics"
)
CLOUD_MONITORING CLOUD_MONITORING
@goEnum( @goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryCloudMonitoring" value: "go.probo.inc/probo/pkg/coredata.VendorCategoryCloudMonitoring"
@@ -389,28 +386,21 @@ enum VendorCategory
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryEmployeeManagement" value: "go.probo.inc/probo/pkg/coredata.VendorCategoryEmployeeManagement"
) )
ENGINEERING ENGINEERING
@goEnum( @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryEngineering")
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryEngineering"
)
FINANCE FINANCE
@goEnum( @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryFinance")
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryFinance"
)
IDENTITY_PROVIDER IDENTITY_PROVIDER
@goEnum( @goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryIdentityProvider" value: "go.probo.inc/probo/pkg/coredata.VendorCategoryIdentityProvider"
) )
IT @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryIT") IT @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryIT")
MARKETING MARKETING
@goEnum( @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryMarketing")
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryMarketing"
)
OFFICE_OPERATIONS OFFICE_OPERATIONS
@goEnum( @goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryOfficeOperations" value: "go.probo.inc/probo/pkg/coredata.VendorCategoryOfficeOperations"
) )
OTHER OTHER @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryOther")
@goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryOther")
PASSWORD_MANAGEMENT PASSWORD_MANAGEMENT
@goEnum( @goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryPasswordManagement" value: "go.probo.inc/probo/pkg/coredata.VendorCategoryPasswordManagement"
@@ -424,15 +414,10 @@ enum VendorCategory
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryProfessionalServices" value: "go.probo.inc/probo/pkg/coredata.VendorCategoryProfessionalServices"
) )
RECRUITING RECRUITING
@goEnum( @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryRecruiting")
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryRecruiting" SALES @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategorySales")
)
SALES
@goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategorySales")
SECURITY SECURITY
@goEnum( @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategorySecurity")
value: "go.probo.inc/probo/pkg/coredata.VendorCategorySecurity"
)
VERSION_CONTROL VERSION_CONTROL
@goEnum( @goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryVersionControl" value: "go.probo.inc/probo/pkg/coredata.VendorCategoryVersionControl"
@@ -542,7 +527,7 @@ type TrustCenter implements Node {
type TrustCenterAccess implements Node { type TrustCenterAccess implements Node {
id: ID! id: ID!
email: String! email: EmailAddr!
name: String! name: String!
createdAt: Datetime! createdAt: Datetime!
updatedAt: Datetime! updatedAt: Datetime!
@@ -550,7 +535,7 @@ type TrustCenterAccess implements Node {
input RequestAllAccessesInput { input RequestAllAccessesInput {
trustCenterId: ID! trustCenterId: ID!
email: String email: EmailAddr
name: String name: String
} }
@@ -573,21 +558,21 @@ input AcceptNonDisclosureAgreementInput {
input RequestDocumentAccessInput { input RequestDocumentAccessInput {
trustCenterId: ID! trustCenterId: ID!
documentId: ID! documentId: ID!
email: String email: EmailAddr
name: String name: String
} }
input RequestReportAccessInput { input RequestReportAccessInput {
trustCenterId: ID! trustCenterId: ID!
reportId: ID! reportId: ID!
email: String email: EmailAddr
name: String name: String
} }
input RequestTrustCenterFileAccessInput { input RequestTrustCenterFileAccessInput {
trustCenterId: ID! trustCenterId: ID!
trustCenterFileId: ID! trustCenterFileId: ID!
email: String email: EmailAddr
name: String name: String
} }

View File

@@ -18,10 +18,12 @@ import (
"github.com/vektah/gqlparser/v2/ast" "github.com/vektah/gqlparser/v2/ast"
"go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/page"
"go.probo.inc/probo/pkg/server/api/trust/v1/types" "go.probo.inc/probo/pkg/server/api/trust/v1/types"
"go.probo.inc/probo/pkg/server/gqlutils/types/cursor" "go.probo.inc/probo/pkg/server/gqlutils/types/cursor"
gid1 "go.probo.inc/probo/pkg/server/gqlutils/types/gid" gid1 "go.probo.inc/probo/pkg/server/gqlutils/types/gid"
mail1 "go.probo.inc/probo/pkg/server/gqlutils/types/mail"
) )
// region ************************** generated!.gotpl ************************** // region ************************** generated!.gotpl **************************
@@ -1115,8 +1117,9 @@ enum Role {
USER USER
} }
scalar Datetime
scalar CursorKey scalar CursorKey
scalar Datetime
scalar EmailAddr
interface Node { interface Node {
id: ID! id: ID!
@@ -1142,11 +1145,9 @@ type Organization implements Node {
enum DocumentType enum DocumentType
@goModel(model: "go.probo.inc/probo/pkg/coredata.DocumentType") { @goModel(model: "go.probo.inc/probo/pkg/coredata.DocumentType") {
OTHER OTHER @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeOther")
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeOther")
ISMS @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeISMS") ISMS @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeISMS")
POLICY POLICY @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypePolicy")
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypePolicy")
PROCEDURE PROCEDURE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeProcedure") @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeProcedure")
} }
@@ -1453,9 +1454,7 @@ enum CountryCode
enum VendorCategory enum VendorCategory
@goModel(model: "go.probo.inc/probo/pkg/coredata.VendorCategory") { @goModel(model: "go.probo.inc/probo/pkg/coredata.VendorCategory") {
ANALYTICS ANALYTICS
@goEnum( @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryAnalytics")
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryAnalytics"
)
CLOUD_MONITORING CLOUD_MONITORING
@goEnum( @goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryCloudMonitoring" value: "go.probo.inc/probo/pkg/coredata.VendorCategoryCloudMonitoring"
@@ -1485,28 +1484,21 @@ enum VendorCategory
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryEmployeeManagement" value: "go.probo.inc/probo/pkg/coredata.VendorCategoryEmployeeManagement"
) )
ENGINEERING ENGINEERING
@goEnum( @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryEngineering")
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryEngineering"
)
FINANCE FINANCE
@goEnum( @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryFinance")
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryFinance"
)
IDENTITY_PROVIDER IDENTITY_PROVIDER
@goEnum( @goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryIdentityProvider" value: "go.probo.inc/probo/pkg/coredata.VendorCategoryIdentityProvider"
) )
IT @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryIT") IT @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryIT")
MARKETING MARKETING
@goEnum( @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryMarketing")
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryMarketing"
)
OFFICE_OPERATIONS OFFICE_OPERATIONS
@goEnum( @goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryOfficeOperations" value: "go.probo.inc/probo/pkg/coredata.VendorCategoryOfficeOperations"
) )
OTHER OTHER @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryOther")
@goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryOther")
PASSWORD_MANAGEMENT PASSWORD_MANAGEMENT
@goEnum( @goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryPasswordManagement" value: "go.probo.inc/probo/pkg/coredata.VendorCategoryPasswordManagement"
@@ -1520,15 +1512,10 @@ enum VendorCategory
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryProfessionalServices" value: "go.probo.inc/probo/pkg/coredata.VendorCategoryProfessionalServices"
) )
RECRUITING RECRUITING
@goEnum( @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategoryRecruiting")
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryRecruiting" SALES @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategorySales")
)
SALES
@goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategorySales")
SECURITY SECURITY
@goEnum( @goEnum(value: "go.probo.inc/probo/pkg/coredata.VendorCategorySecurity")
value: "go.probo.inc/probo/pkg/coredata.VendorCategorySecurity"
)
VERSION_CONTROL VERSION_CONTROL
@goEnum( @goEnum(
value: "go.probo.inc/probo/pkg/coredata.VendorCategoryVersionControl" value: "go.probo.inc/probo/pkg/coredata.VendorCategoryVersionControl"
@@ -1638,7 +1625,7 @@ type TrustCenter implements Node {
type TrustCenterAccess implements Node { type TrustCenterAccess implements Node {
id: ID! id: ID!
email: String! email: EmailAddr!
name: String! name: String!
createdAt: Datetime! createdAt: Datetime!
updatedAt: Datetime! updatedAt: Datetime!
@@ -1646,7 +1633,7 @@ type TrustCenterAccess implements Node {
input RequestAllAccessesInput { input RequestAllAccessesInput {
trustCenterId: ID! trustCenterId: ID!
email: String email: EmailAddr
name: String name: String
} }
@@ -1669,21 +1656,21 @@ input AcceptNonDisclosureAgreementInput {
input RequestDocumentAccessInput { input RequestDocumentAccessInput {
trustCenterId: ID! trustCenterId: ID!
documentId: ID! documentId: ID!
email: String email: EmailAddr
name: String name: String
} }
input RequestReportAccessInput { input RequestReportAccessInput {
trustCenterId: ID! trustCenterId: ID!
reportId: ID! reportId: ID!
email: String email: EmailAddr
name: String name: String
} }
input RequestTrustCenterFileAccessInput { input RequestTrustCenterFileAccessInput {
trustCenterId: ID! trustCenterId: ID!
trustCenterFileId: ID! trustCenterFileId: ID!
email: String email: EmailAddr
name: String name: String
} }
@@ -4584,7 +4571,7 @@ func (ec *executionContext) _TrustCenterAccess_email(ctx context.Context, field
return obj.Email, nil return obj.Email, nil
}, },
nil, nil,
ec.marshalNString2string, ec.marshalNEmailAddr2goᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr,
true, true,
true, true,
) )
@@ -4597,7 +4584,7 @@ func (ec *executionContext) fieldContext_TrustCenterAccess_email(_ context.Conte
IsMethod: false, IsMethod: false,
IsResolver: false, IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields") return nil, errors.New("field of type EmailAddr does not have child fields")
}, },
} }
return fc, nil return fc, nil
@@ -7165,7 +7152,7 @@ func (ec *executionContext) unmarshalInputRequestAllAccessesInput(ctx context.Co
it.TrustCenterID = data it.TrustCenterID = data
case "email": case "email":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email"))
data, err := ec.unmarshalOString2ᚖstring(ctx, v) data, err := ec.unmarshalOEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, v)
if err != nil { if err != nil {
return it, err return it, err
} }
@@ -7213,7 +7200,7 @@ func (ec *executionContext) unmarshalInputRequestDocumentAccessInput(ctx context
it.DocumentID = data it.DocumentID = data
case "email": case "email":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email"))
data, err := ec.unmarshalOString2ᚖstring(ctx, v) data, err := ec.unmarshalOEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, v)
if err != nil { if err != nil {
return it, err return it, err
} }
@@ -7261,7 +7248,7 @@ func (ec *executionContext) unmarshalInputRequestReportAccessInput(ctx context.C
it.ReportID = data it.ReportID = data
case "email": case "email":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email"))
data, err := ec.unmarshalOString2ᚖstring(ctx, v) data, err := ec.unmarshalOEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, v)
if err != nil { if err != nil {
return it, err return it, err
} }
@@ -7309,7 +7296,7 @@ func (ec *executionContext) unmarshalInputRequestTrustCenterFileAccessInput(ctx
it.TrustCenterFileID = data it.TrustCenterFileID = data
case "email": case "email":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email"))
data, err := ec.unmarshalOString2ᚖstring(ctx, v) data, err := ec.unmarshalOEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx, v)
if err != nil { if err != nil {
return it, err return it, err
} }
@@ -10647,6 +10634,22 @@ var (
} }
) )
func (ec *executionContext) unmarshalNEmailAddr2goᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx context.Context, v any) (mail.Addr, error) {
res, err := mail1.UnmarshalAddrScalar(v)
return res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) marshalNEmailAddr2goᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx context.Context, sel ast.SelectionSet, v mail.Addr) graphql.Marshaler {
_ = sel
res := mail1.MarshalAddrScalar(v)
if res == graphql.Null {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "the requested element is null which the schema does not allow")
}
}
return res
}
func (ec *executionContext) unmarshalNExportDocumentPDFInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐExportDocumentPDFInput(ctx context.Context, v any) (types.ExportDocumentPDFInput, error) { func (ec *executionContext) unmarshalNExportDocumentPDFInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐExportDocumentPDFInput(ctx context.Context, v any) (types.ExportDocumentPDFInput, error) {
res, err := ec.unmarshalInputExportDocumentPDFInput(ctx, v) res, err := ec.unmarshalInputExportDocumentPDFInput(ctx, v)
return res, graphql.ErrorOnPath(ctx, err) return res, graphql.ErrorOnPath(ctx, err)
@@ -11431,6 +11434,24 @@ func (ec *executionContext) marshalOCursorKey2ᚖgoᚗproboᚗincᚋproboᚋpkg
return res return res
} }
func (ec *executionContext) unmarshalOEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx context.Context, v any) (*mail.Addr, error) {
if v == nil {
return nil, nil
}
res, err := mail1.UnmarshalAddrScalar(v)
return &res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) marshalOEmailAddr2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋmailᚐAddr(ctx context.Context, sel ast.SelectionSet, v *mail.Addr) graphql.Marshaler {
if v == nil {
return graphql.Null
}
_ = sel
_ = ctx
res := mail1.MarshalAddrScalar(*v)
return res
}
func (ec *executionContext) unmarshalOInt2ᚖint(ctx context.Context, v any) (*int, error) { func (ec *executionContext) unmarshalOInt2ᚖint(ctx context.Context, v any) (*int, error) {
if v == nil { if v == nil {
return nil, nil return nil, nil

View File

@@ -21,20 +21,17 @@ import (
"github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql"
"go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/server/api/trust/v1/types" "go.probo.inc/probo/pkg/server/api/trust/v1/types"
) )
type TokenAccessData struct { type TokenAccessData struct {
TrustCenterID gid.GID TrustCenterID gid.GID
Email string Email mail.Addr
TenantID gid.TenantID TenantID gid.TenantID
Scope string Scope string
} }
func (t *TokenAccessData) GetEmail() string {
return t.Email
}
type ContextAccessor interface { type ContextAccessor interface {
UserFromContext(ctx context.Context) *coredata.User UserFromContext(ctx context.Context) *coredata.User
TokenAccessFromContext(ctx context.Context) *TokenAccessData TokenAccessFromContext(ctx context.Context) *TokenAccessData

View File

@@ -11,6 +11,7 @@ import (
"go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/page"
) )
@@ -140,30 +141,30 @@ type RequestAccessesPayload struct {
} }
type RequestAllAccessesInput struct { type RequestAllAccessesInput struct {
TrustCenterID gid.GID `json:"trustCenterId"` TrustCenterID gid.GID `json:"trustCenterId"`
Email *string `json:"email,omitempty"` Email *mail.Addr `json:"email,omitempty"`
Name *string `json:"name,omitempty"` Name *string `json:"name,omitempty"`
} }
type RequestDocumentAccessInput struct { type RequestDocumentAccessInput struct {
TrustCenterID gid.GID `json:"trustCenterId"` TrustCenterID gid.GID `json:"trustCenterId"`
DocumentID gid.GID `json:"documentId"` DocumentID gid.GID `json:"documentId"`
Email *string `json:"email,omitempty"` Email *mail.Addr `json:"email,omitempty"`
Name *string `json:"name,omitempty"` Name *string `json:"name,omitempty"`
} }
type RequestReportAccessInput struct { type RequestReportAccessInput struct {
TrustCenterID gid.GID `json:"trustCenterId"` TrustCenterID gid.GID `json:"trustCenterId"`
ReportID gid.GID `json:"reportId"` ReportID gid.GID `json:"reportId"`
Email *string `json:"email,omitempty"` Email *mail.Addr `json:"email,omitempty"`
Name *string `json:"name,omitempty"` Name *string `json:"name,omitempty"`
} }
type RequestTrustCenterFileAccessInput struct { type RequestTrustCenterFileAccessInput struct {
TrustCenterID gid.GID `json:"trustCenterId"` TrustCenterID gid.GID `json:"trustCenterId"`
TrustCenterFileID gid.GID `json:"trustCenterFileId"` TrustCenterFileID gid.GID `json:"trustCenterFileId"`
Email *string `json:"email,omitempty"` Email *mail.Addr `json:"email,omitempty"`
Name *string `json:"name,omitempty"` Name *string `json:"name,omitempty"`
} }
type TrustCenter struct { type TrustCenter struct {
@@ -187,7 +188,7 @@ func (this TrustCenter) GetID() gid.GID { return this.ID }
type TrustCenterAccess struct { type TrustCenterAccess struct {
ID gid.GID `json:"id"` ID gid.GID `json:"id"`
Email string `json:"email"` Email mail.Addr `json:"email"`
Name string `json:"name"` Name string `json:"name"`
CreatedAt time.Time `json:"createdAt"` CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"` UpdatedAt time.Time `json:"updatedAt"`

View File

@@ -13,6 +13,7 @@ import (
"github.com/vektah/gqlparser/v2/gqlerror" "github.com/vektah/gqlparser/v2/gqlerror"
"go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/page"
"go.probo.inc/probo/pkg/server/api/trust/v1/schema" "go.probo.inc/probo/pkg/server/api/trust/v1/schema"
"go.probo.inc/probo/pkg/server/api/trust/v1/types" "go.probo.inc/probo/pkg/server/api/trust/v1/types"
@@ -82,7 +83,7 @@ func (r *documentResolver) IsUserAuthorized(ctx context.Context, obj *types.Docu
tokenData := TokenAccessFromContext(ctx) tokenData := TokenAccessFromContext(ctx)
if tokenData != nil { if tokenData != nil {
documentAccess, err := privateTrustService.TrustCenterAccesses.LoadDocumentAccess(ctx, tokenData.TrustCenterID, tokenData.GetEmail(), obj.ID) documentAccess, err := privateTrustService.TrustCenterAccesses.LoadDocumentAccess(ctx, tokenData.TrustCenterID, tokenData.Email, obj.ID)
if err != nil { if err != nil {
return false, nil return false, nil
} }
@@ -108,7 +109,7 @@ func (r *documentResolver) HasUserRequestedAccess(ctx context.Context, obj *type
tokenData := TokenAccessFromContext(ctx) tokenData := TokenAccessFromContext(ctx)
if tokenData != nil { if tokenData != nil {
// Try to load document access - if it exists (regardless of active status), user has requested it // Try to load document access - if it exists (regardless of active status), user has requested it
_, err := privateTrustService.TrustCenterAccesses.LoadDocumentAccess(ctx, tokenData.TrustCenterID, tokenData.GetEmail(), obj.ID) _, err := privateTrustService.TrustCenterAccesses.LoadDocumentAccess(ctx, tokenData.TrustCenterID, tokenData.Email, obj.ID)
if err != nil { if err != nil {
return false, nil // No access requested or error return false, nil // No access requested or error
} }
@@ -133,8 +134,8 @@ func (r *mutationResolver) RequestAllAccesses(ctx context.Context, input types.R
if email != nil || input.Name != nil { if email != nil || input.Name != nil {
return nil, fmt.Errorf("email and name are not allowed for authenticated users") return nil, fmt.Errorf("email and name are not allowed for authenticated users")
} }
emailValue := tokenData.GetEmail()
email = &emailValue *email = tokenData.Email
} }
if email == nil { if email == nil {
return nil, fmt.Errorf("email is required for unauthenticated users") return nil, fmt.Errorf("email is required for unauthenticated users")
@@ -202,13 +203,13 @@ func (r *mutationResolver) ExportDocumentPDF(ctx context.Context, input types.Ex
if ndaExists { if ndaExists {
tokenData := TokenAccessFromContext(ctx) tokenData := TokenAccessFromContext(ctx)
hasAcceptedNDA, err = privateTrustService.TrustCenterAccesses.HasAcceptedNonDisclosureAgreement(ctx, tokenData.TrustCenterID, tokenData.GetEmail()) hasAcceptedNDA, err = privateTrustService.TrustCenterAccesses.HasAcceptedNonDisclosureAgreement(ctx, tokenData.TrustCenterID, tokenData.Email)
if err != nil { if err != nil {
panic(fmt.Errorf("cannot check if user has accepted NDA: %w", err)) panic(fmt.Errorf("cannot check if user has accepted NDA: %w", err))
} }
} }
documentAccess, err := privateTrustService.TrustCenterAccesses.LoadDocumentAccess(ctx, tokenData.TrustCenterID, tokenData.GetEmail(), input.DocumentID) documentAccess, err := privateTrustService.TrustCenterAccesses.LoadDocumentAccess(ctx, tokenData.TrustCenterID, tokenData.Email, input.DocumentID)
if err != nil { if err != nil {
panic(fmt.Errorf("cannot check document access: %w", err)) panic(fmt.Errorf("cannot check document access: %w", err))
} }
@@ -223,12 +224,12 @@ func (r *mutationResolver) ExportDocumentPDF(ctx context.Context, input types.Ex
} }
userData := UserFromContext(ctx) userData := UserFromContext(ctx)
userEmail := "" var userEmail mail.Addr
if userData != nil { if userData != nil {
userEmail = userData.EmailAddress userEmail = userData.EmailAddress
} }
if tokenData != nil { if tokenData != nil {
userEmail = tokenData.GetEmail() userEmail = tokenData.Email
} }
pdf, err := privateTrustService.Documents.ExportPDF(ctx, input.DocumentID, userEmail) pdf, err := privateTrustService.Documents.ExportPDF(ctx, input.DocumentID, userEmail)
@@ -280,13 +281,13 @@ func (r *mutationResolver) ExportReportPDF(ctx context.Context, input types.Expo
} }
if ndaExists { if ndaExists {
hasAcceptedNDA, err = privateTrustService.TrustCenterAccesses.HasAcceptedNonDisclosureAgreement(ctx, tokenData.TrustCenterID, tokenData.GetEmail()) hasAcceptedNDA, err = privateTrustService.TrustCenterAccesses.HasAcceptedNonDisclosureAgreement(ctx, tokenData.TrustCenterID, tokenData.Email)
if err != nil { if err != nil {
panic(fmt.Errorf("cannot check if user has accepted NDA: %w", err)) panic(fmt.Errorf("cannot check if user has accepted NDA: %w", err))
} }
} }
reportAccess, err := privateTrustService.TrustCenterAccesses.LoadReportAccess(ctx, tokenData.TrustCenterID, tokenData.GetEmail(), input.ReportID) reportAccess, err := privateTrustService.TrustCenterAccesses.LoadReportAccess(ctx, tokenData.TrustCenterID, tokenData.Email, input.ReportID)
if err != nil { if err != nil {
panic(fmt.Errorf("cannot check report access: %w", err)) panic(fmt.Errorf("cannot check report access: %w", err))
} }
@@ -301,12 +302,12 @@ func (r *mutationResolver) ExportReportPDF(ctx context.Context, input types.Expo
} }
userData := UserFromContext(ctx) userData := UserFromContext(ctx)
userEmail := "" var userEmail mail.Addr
if userData != nil { if userData != nil {
userEmail = userData.EmailAddress userEmail = userData.EmailAddress
} }
if tokenData != nil { if tokenData != nil {
userEmail = tokenData.GetEmail() userEmail = tokenData.Email
} }
pdf, err := privateTrustService.Reports.ExportPDF(ctx, input.ReportID, userEmail) pdf, err := privateTrustService.Reports.ExportPDF(ctx, input.ReportID, userEmail)
@@ -331,7 +332,7 @@ func (r *mutationResolver) AcceptNonDisclosureAgreement(ctx context.Context, inp
return nil, fmt.Errorf("token not found") return nil, fmt.Errorf("token not found")
} }
err = privateTrustService.TrustCenterAccesses.AcceptNonDisclosureAgreement(ctx, input.TrustCenterID, tokenData.GetEmail()) err = privateTrustService.TrustCenterAccesses.AcceptNonDisclosureAgreement(ctx, input.TrustCenterID, tokenData.Email)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot accept NDA: %w", err) return nil, fmt.Errorf("cannot accept NDA: %w", err)
} }
@@ -360,11 +361,7 @@ func (r *mutationResolver) RequestDocumentAccess(ctx context.Context, input type
email := input.Email email := input.Email
tokenData := TokenAccessFromContext(ctx) tokenData := TokenAccessFromContext(ctx)
if tokenData != nil { if tokenData != nil {
if email != nil || input.Name != nil { *email = tokenData.Email
return nil, fmt.Errorf("email and name are not allowed for authenticated users")
}
emailValue := tokenData.GetEmail()
email = &emailValue
} }
if email == nil { if email == nil {
return nil, fmt.Errorf("email is required for unauthenticated users") return nil, fmt.Errorf("email is required for unauthenticated users")
@@ -413,11 +410,7 @@ func (r *mutationResolver) RequestReportAccess(ctx context.Context, input types.
email := input.Email email := input.Email
tokenData := TokenAccessFromContext(ctx) tokenData := TokenAccessFromContext(ctx)
if tokenData != nil { if tokenData != nil {
if email != nil || input.Name != nil { *email = tokenData.Email
return nil, fmt.Errorf("email and name are not allowed for authenticated users")
}
emailValue := tokenData.GetEmail()
email = &emailValue
} }
if email == nil { if email == nil {
return nil, fmt.Errorf("email is required for unauthenticated users") return nil, fmt.Errorf("email is required for unauthenticated users")
@@ -466,11 +459,7 @@ func (r *mutationResolver) RequestTrustCenterFileAccess(ctx context.Context, inp
email := input.Email email := input.Email
tokenData := TokenAccessFromContext(ctx) tokenData := TokenAccessFromContext(ctx)
if tokenData != nil { if tokenData != nil {
if email != nil || input.Name != nil { *email = tokenData.Email
return nil, fmt.Errorf("email and name are not allowed for authenticated users")
}
emailValue := tokenData.GetEmail()
email = &emailValue
} }
if email == nil { if email == nil {
return nil, fmt.Errorf("email is required for unauthenticated users") return nil, fmt.Errorf("email is required for unauthenticated users")
@@ -538,13 +527,13 @@ func (r *mutationResolver) ExportTrustCenterFile(ctx context.Context, input type
} }
if ndaExists { if ndaExists {
hasAcceptedNDA, err = privateTrustService.TrustCenterAccesses.HasAcceptedNonDisclosureAgreement(ctx, tokenData.TrustCenterID, tokenData.GetEmail()) hasAcceptedNDA, err = privateTrustService.TrustCenterAccesses.HasAcceptedNonDisclosureAgreement(ctx, tokenData.TrustCenterID, tokenData.Email)
if err != nil { if err != nil {
panic(fmt.Errorf("cannot check if user has accepted NDA: %w", err)) panic(fmt.Errorf("cannot check if user has accepted NDA: %w", err))
} }
} }
fileAccess, err := privateTrustService.TrustCenterAccesses.LoadTrustCenterFileAccess(ctx, tokenData.TrustCenterID, tokenData.GetEmail(), input.TrustCenterFileID) fileAccess, err := privateTrustService.TrustCenterAccesses.LoadTrustCenterFileAccess(ctx, tokenData.TrustCenterID, tokenData.Email, input.TrustCenterFileID)
if err != nil { if err != nil {
panic(fmt.Errorf("cannot check trust center file access: %w", err)) panic(fmt.Errorf("cannot check trust center file access: %w", err))
} }
@@ -559,12 +548,12 @@ func (r *mutationResolver) ExportTrustCenterFile(ctx context.Context, input type
} }
userData := UserFromContext(ctx) userData := UserFromContext(ctx)
userEmail := "" var userEmail mail.Addr
if userData != nil { if userData != nil {
userEmail = userData.EmailAddress userEmail = userData.EmailAddress
} }
if tokenData != nil { if tokenData != nil {
userEmail = tokenData.GetEmail() userEmail = tokenData.Email
} }
fileData, err := privateTrustService.TrustCenterFiles.ExportFile(ctx, input.TrustCenterFileID, userEmail) fileData, err := privateTrustService.TrustCenterFiles.ExportFile(ctx, input.TrustCenterFileID, userEmail)
@@ -743,7 +732,7 @@ func (r *reportResolver) IsUserAuthorized(ctx context.Context, obj *types.Report
tokenData := TokenAccessFromContext(ctx) tokenData := TokenAccessFromContext(ctx)
if tokenData != nil { if tokenData != nil {
reportAccess, err := privateTrustService.TrustCenterAccesses.LoadReportAccess(ctx, tokenData.TrustCenterID, tokenData.GetEmail(), obj.ID) reportAccess, err := privateTrustService.TrustCenterAccesses.LoadReportAccess(ctx, tokenData.TrustCenterID, tokenData.Email, obj.ID)
if err != nil { if err != nil {
return false, nil return false, nil
} }
@@ -768,7 +757,7 @@ func (r *reportResolver) HasUserRequestedAccess(ctx context.Context, obj *types.
tokenData := TokenAccessFromContext(ctx) tokenData := TokenAccessFromContext(ctx)
if tokenData != nil { if tokenData != nil {
_, err := privateTrustService.TrustCenterAccesses.LoadReportAccess(ctx, tokenData.TrustCenterID, tokenData.GetEmail(), obj.ID) _, err := privateTrustService.TrustCenterAccesses.LoadReportAccess(ctx, tokenData.TrustCenterID, tokenData.Email, obj.ID)
if err != nil { if err != nil {
return false, nil return false, nil
} }
@@ -822,7 +811,7 @@ func (r *trustCenterResolver) HasAcceptedNonDisclosureAgreement(ctx context.Cont
tokenData := TokenAccessFromContext(ctx) tokenData := TokenAccessFromContext(ctx)
if tokenData != nil { if tokenData != nil {
hasAcceptedNDA, err := privateTrustService.TrustCenterAccesses.HasAcceptedNonDisclosureAgreement(ctx, obj.ID, tokenData.GetEmail()) hasAcceptedNDA, err := privateTrustService.TrustCenterAccesses.HasAcceptedNonDisclosureAgreement(ctx, obj.ID, tokenData.Email)
if err != nil { if err != nil {
panic(fmt.Errorf("cannot check if user has accepted NDA: %w", err)) panic(fmt.Errorf("cannot check if user has accepted NDA: %w", err))
} }
@@ -947,7 +936,7 @@ func (r *trustCenterFileResolver) IsUserAuthorized(ctx context.Context, obj *typ
tokenData := TokenAccessFromContext(ctx) tokenData := TokenAccessFromContext(ctx)
if tokenData != nil { if tokenData != nil {
fileAccess, err := privateTrustService.TrustCenterAccesses.LoadTrustCenterFileAccess(ctx, tokenData.TrustCenterID, tokenData.GetEmail(), obj.ID) fileAccess, err := privateTrustService.TrustCenterAccesses.LoadTrustCenterFileAccess(ctx, tokenData.TrustCenterID, tokenData.Email, obj.ID)
if err != nil { if err != nil {
return false, nil return false, nil
} }
@@ -972,7 +961,7 @@ func (r *trustCenterFileResolver) HasUserRequestedAccess(ctx context.Context, ob
tokenData := TokenAccessFromContext(ctx) tokenData := TokenAccessFromContext(ctx)
if tokenData != nil { if tokenData != nil {
_, err := privateTrustService.TrustCenterAccesses.LoadTrustCenterFileAccess(ctx, tokenData.TrustCenterID, tokenData.GetEmail(), obj.ID) _, err := privateTrustService.TrustCenterAccesses.LoadTrustCenterFileAccess(ctx, tokenData.TrustCenterID, tokenData.Email, obj.ID)
if err != nil { if err != nil {
return false, nil return false, nil
} }

View File

@@ -87,9 +87,7 @@ func AcceptInvitationHandler(authSvc *authsvc.Service, authzSvc *authz.Service,
return return
} }
response := AcceptInvitationResponse{ response := AcceptInvitationResponse(req)
InvitationID: req.InvitationID,
}
httpserver.RenderJSON(w, http.StatusOK, response) httpserver.RenderJSON(w, http.StatusOK, response)
} }

View File

@@ -75,9 +75,7 @@ func DeleteUserAPIKeyHandler(authSvc *authsvc.Service) http.HandlerFunc {
return return
} }
response := DeleteUserAPIKeyResponse{ response := DeleteUserAPIKeyResponse(req)
ID: req.ID,
}
httpserver.RenderJSON(w, http.StatusOK, response) httpserver.RenderJSON(w, http.StatusOK, response)
} }

View File

@@ -19,13 +19,14 @@ import (
"fmt" "fmt"
"net/http" "net/http"
authsvc "go.probo.inc/probo/pkg/auth"
"go.gearno.de/kit/httpserver" "go.gearno.de/kit/httpserver"
authsvc "go.probo.inc/probo/pkg/auth"
"go.probo.inc/probo/pkg/mail"
) )
type ( type (
ForgetPasswordRequest struct { ForgetPasswordRequest struct {
Email string `json:"email"` Email mail.Addr `json:"email"`
} }
ForgetPasswordResponse struct { ForgetPasswordResponse struct {

View File

@@ -18,9 +18,10 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"go.gearno.de/kit/httpserver"
"go.probo.inc/probo/pkg/authz" "go.probo.inc/probo/pkg/authz"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.gearno.de/kit/httpserver" "go.probo.inc/probo/pkg/mail"
) )
type ( type (
@@ -30,7 +31,7 @@ type (
InvitationResponse struct { InvitationResponse struct {
ID gid.GID `json:"id"` ID gid.GID `json:"id"`
Email string `json:"email"` Email mail.Addr `json:"email"`
FullName string `json:"fullName"` FullName string `json:"fullName"`
Role string `json:"role"` Role string `json:"role"`
ExpiresAt string `json:"expiresAt"` ExpiresAt string `json:"expiresAt"`

View File

@@ -25,13 +25,14 @@ import (
authsvc "go.probo.inc/probo/pkg/auth" authsvc "go.probo.inc/probo/pkg/auth"
"go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/securecookie" "go.probo.inc/probo/pkg/securecookie"
) )
type ( type (
SignInRequest struct { SignInRequest struct {
Email string `json:"email"` Email mail.Addr `json:"email"`
Password string `json:"password"` Password string `json:"password"`
} }
SignInResponse struct { SignInResponse struct {
@@ -40,7 +41,7 @@ type (
UserResponse struct { UserResponse struct {
ID gid.GID `json:"id"` ID gid.GID `json:"id"`
Email string `json:"email"` Email mail.Addr `json:"email"`
FullName string `json:"fullName"` FullName string `json:"fullName"`
CreatedAt time.Time `json:"createdAt"` CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"` UpdatedAt time.Time `json:"updatedAt"`

View File

@@ -22,14 +22,15 @@ import (
"go.gearno.de/kit/httpserver" "go.gearno.de/kit/httpserver"
authsvc "go.probo.inc/probo/pkg/auth" authsvc "go.probo.inc/probo/pkg/auth"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/securecookie" "go.probo.inc/probo/pkg/securecookie"
) )
type ( type (
SignUpRequest struct { SignUpRequest struct {
Email string `json:"email"` Email mail.Addr `json:"email"`
Password string `json:"password"` Password string `json:"password"`
FullName string `json:"fullName"` FullName string `json:"fullName"`
} }
SignUpResponse struct { SignUpResponse struct {

View File

@@ -0,0 +1,46 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package mail
import (
"errors"
"io"
"strconv"
"github.com/99designs/gqlgen/graphql"
"go.probo.inc/probo/pkg/mail"
)
type AddrScalar = mail.Addr
func MarshalAddrScalar(a mail.Addr) graphql.Marshaler {
return graphql.WriterFunc(func(w io.Writer) {
w.Write([]byte(strconv.Quote(a.String())))
})
}
func UnmarshalAddrScalar(v interface{}) (mail.Addr, error) {
s, ok := v.(string)
if !ok {
return mail.Nil, errors.New("must be a string")
}
a, err := mail.ParseAddr(s)
if err != nil {
return mail.Nil, err
}
return a, nil
}

View File

@@ -26,6 +26,7 @@ import (
"go.probo.inc/probo/pkg/baseurl" "go.probo.inc/probo/pkg/baseurl"
"go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
) )
const ( const (
@@ -101,7 +102,7 @@ func (s *SlackMessageService) UpdateSlackAccessMessage(
ctx context.Context, ctx context.Context,
slackMessageID gid.GID, slackMessageID gid.GID,
responseURL string, responseURL string,
requesterEmail string, requesterEmail mail.Addr,
) error { ) error {
return s.svc.pg.WithTx(ctx, func(tx pg.Conn) error { return s.svc.pg.WithTx(ctx, func(tx pg.Conn) error {
var slackMessage coredata.SlackMessage var slackMessage coredata.SlackMessage
@@ -174,7 +175,7 @@ func (s *SlackMessageService) UpdateSlackAccessMessage(
func (s *SlackMessageService) QueueSlackNotification( func (s *SlackMessageService) QueueSlackNotification(
ctx context.Context, ctx context.Context,
requesterEmail string, requesterEmail mail.Addr,
trustCenterID gid.GID, trustCenterID gid.GID,
) error { ) error {
return s.svc.pg.WithTx(ctx, func(tx pg.Conn) error { return s.svc.pg.WithTx(ctx, func(tx pg.Conn) error {
@@ -287,7 +288,7 @@ func (s *SlackMessageService) QueueSlackNotification(
func (s *SlackMessageService) QueueSlackAccessMessageUpdate( func (s *SlackMessageService) QueueSlackAccessMessageUpdate(
ctx context.Context, ctx context.Context,
requesterEmail string, requesterEmail mail.Addr,
trustCenterID gid.GID, trustCenterID gid.GID,
) error { ) error {
return s.svc.pg.WithTx(ctx, func(tx pg.Conn) error { return s.svc.pg.WithTx(ctx, func(tx pg.Conn) error {
@@ -461,7 +462,7 @@ func (s *SlackMessageService) loadDocumentsReportsAndFilesFromAccesses(
func (s *SlackMessageService) buildAccessRequestMessage( func (s *SlackMessageService) buildAccessRequestMessage(
slackMessageID gid.GID, slackMessageID gid.GID,
requesterName string, requesterName string,
requesterEmail string, requesterEmail mail.Addr,
organizationID gid.GID, organizationID gid.GID,
documents []SlackMessageDocument, documents []SlackMessageDocument,
reports []SlackMessageReport, reports []SlackMessageReport,
@@ -483,7 +484,7 @@ func (s *SlackMessageService) buildAccessRequestMessage(
Files []SlackMessageFile Files []SlackMessageFile
}{ }{
RequesterName: requesterName, RequesterName: requesterName,
RequesterEmail: requesterEmail, RequesterEmail: requesterEmail.String(),
OrganizationID: organizationID.String(), OrganizationID: organizationID.String(),
Domain: base.Host(), Domain: base.Host(),
SlackMessageID: slackMessageID.String(), SlackMessageID: slackMessageID.String(),

View File

@@ -19,13 +19,14 @@ import (
"fmt" "fmt"
"io" "io"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/docgen" "go.probo.inc/probo/pkg/docgen"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/html2pdf" "go.probo.inc/probo/pkg/html2pdf"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/page"
"go.probo.inc/probo/pkg/watermarkpdf" "go.probo.inc/probo/pkg/watermarkpdf"
"go.gearno.de/kit/pg"
) )
type ( type (
@@ -88,7 +89,7 @@ func (s *DocumentService) ListForOrganizationId(
func (s *DocumentService) ExportPDF( func (s *DocumentService) ExportPDF(
ctx context.Context, ctx context.Context,
documentID gid.GID, documentID gid.GID,
email string, email mail.Addr,
) ([]byte, error) { ) ([]byte, error) {
pdfData, err := s.exportPDFData(ctx, documentID) pdfData, err := s.exportPDFData(ctx, documentID)
if err != nil { if err != nil {

View File

@@ -22,10 +22,11 @@ import (
"github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/watermarkpdf" "go.probo.inc/probo/pkg/watermarkpdf"
"go.gearno.de/kit/pg"
) )
type ReportService struct { type ReportService struct {
@@ -88,7 +89,7 @@ func (s ReportService) GenerateDownloadURL(
func (s ReportService) ExportPDF( func (s ReportService) ExportPDF(
ctx context.Context, ctx context.Context,
reportID gid.GID, reportID gid.GID,
email string, email mail.Addr,
) ([]byte, error) { ) ([]byte, error) {
pdfData, err := s.exportPDFData(ctx, reportID) pdfData, err := s.exportPDFData(ctx, reportID)
if err != nil { if err != nil {

View File

@@ -19,7 +19,6 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"net/mail"
"net/url" "net/url"
"time" "time"
@@ -29,6 +28,7 @@ import (
"go.probo.inc/probo/pkg/auth" "go.probo.inc/probo/pkg/auth"
"go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/probo" "go.probo.inc/probo/pkg/probo"
"go.probo.inc/probo/pkg/statelesstoken" "go.probo.inc/probo/pkg/statelesstoken"
"go.probo.inc/probo/pkg/validator" "go.probo.inc/probo/pkg/validator"
@@ -43,7 +43,7 @@ type (
TrustCenterAccessRequest struct { TrustCenterAccessRequest struct {
TrustCenterID gid.GID TrustCenterID gid.GID
Email string Email mail.Addr
Name *string Name *string
DocumentIDs []gid.GID DocumentIDs []gid.GID
ReportIDs []gid.GID ReportIDs []gid.GID
@@ -66,7 +66,7 @@ func (tcar *TrustCenterAccessRequest) Validate() error {
func (s TrustCenterAccessService) ValidateToken( func (s TrustCenterAccessService) ValidateToken(
ctx context.Context, ctx context.Context,
trustCenterID gid.GID, trustCenterID gid.GID,
email string, email mail.Addr,
) error { ) error {
return s.svc.pg.WithConn(ctx, func(conn pg.Conn) error { return s.svc.pg.WithConn(ctx, func(conn pg.Conn) error {
access := &coredata.TrustCenterAccess{} access := &coredata.TrustCenterAccess{}
@@ -87,6 +87,10 @@ func (s TrustCenterAccessService) Request(
ctx context.Context, ctx context.Context,
req *TrustCenterAccessRequest, req *TrustCenterAccessRequest,
) (*coredata.TrustCenterAccess, error) { ) (*coredata.TrustCenterAccess, error) {
if err := req.Validate(); err != nil {
return nil, fmt.Errorf("invalid request arguments")
}
now := time.Now() now := time.Now()
var access *coredata.TrustCenterAccess var access *coredata.TrustCenterAccess
@@ -161,10 +165,6 @@ func (s TrustCenterAccessService) Request(
return fmt.Errorf("name is required for new access requests") return fmt.Errorf("name is required for new access requests")
} }
if _, err := mail.ParseAddress(req.Email); err != nil {
return fmt.Errorf("invalid email address")
}
access = &coredata.TrustCenterAccess{ access = &coredata.TrustCenterAccess{
ID: gid.New(s.svc.scope.GetTenantID(), coredata.TrustCenterAccessEntityType), ID: gid.New(s.svc.scope.GetTenantID(), coredata.TrustCenterAccessEntityType),
OrganizationID: organizationID, OrganizationID: organizationID,
@@ -248,7 +248,7 @@ func (s TrustCenterAccessService) Request(
return access, nil return access, nil
} }
func (s TrustCenterAccessService) HasAcceptedNonDisclosureAgreement(ctx context.Context, trustCenterID gid.GID, email string) (bool, error) { func (s TrustCenterAccessService) HasAcceptedNonDisclosureAgreement(ctx context.Context, trustCenterID gid.GID, email mail.Addr) (bool, error) {
access := &coredata.TrustCenterAccess{} access := &coredata.TrustCenterAccess{}
err := s.svc.pg.WithConn(ctx, func(conn pg.Conn) error { err := s.svc.pg.WithConn(ctx, func(conn pg.Conn) error {
err := access.LoadByTrustCenterIDAndEmail(ctx, conn, s.svc.scope, trustCenterID, email) err := access.LoadByTrustCenterIDAndEmail(ctx, conn, s.svc.scope, trustCenterID, email)
@@ -266,7 +266,7 @@ func (s TrustCenterAccessService) HasAcceptedNonDisclosureAgreement(ctx context.
return access.HasAcceptedNonDisclosureAgreement, nil return access.HasAcceptedNonDisclosureAgreement, nil
} }
func (s TrustCenterAccessService) AcceptNonDisclosureAgreement(ctx context.Context, trustCenterID gid.GID, email string) error { func (s TrustCenterAccessService) AcceptNonDisclosureAgreement(ctx context.Context, trustCenterID gid.GID, email mail.Addr) error {
return s.svc.pg.WithTx(ctx, func(tx pg.Conn) error { return s.svc.pg.WithTx(ctx, func(tx pg.Conn) error {
access := &coredata.TrustCenterAccess{} access := &coredata.TrustCenterAccess{}
if err := access.LoadByTrustCenterIDAndEmail(ctx, tx, s.svc.scope, trustCenterID, email); err != nil { if err := access.LoadByTrustCenterIDAndEmail(ctx, tx, s.svc.scope, trustCenterID, email); err != nil {
@@ -279,7 +279,7 @@ func (s TrustCenterAccessService) AcceptNonDisclosureAgreement(ctx context.Conte
} }
acceptationLogs, err := json.Marshal(map[string]string{ acceptationLogs, err := json.Marshal(map[string]string{
"email": email, "email": email.String(),
"timestamp": time.Now().Format(time.RFC3339), "timestamp": time.Now().Format(time.RFC3339),
"ip": ctx.Value(coredata.ContextKeyIPAddress).(string), "ip": ctx.Value(coredata.ContextKeyIPAddress).(string),
}) })
@@ -302,7 +302,7 @@ func (s TrustCenterAccessService) AcceptNonDisclosureAgreement(ctx context.Conte
func (s TrustCenterAccessService) LoadDocumentAccess( func (s TrustCenterAccessService) LoadDocumentAccess(
ctx context.Context, ctx context.Context,
trustCenterID gid.GID, trustCenterID gid.GID,
email string, email mail.Addr,
documentID gid.GID, documentID gid.GID,
) (*coredata.TrustCenterDocumentAccess, error) { ) (*coredata.TrustCenterDocumentAccess, error) {
var documentAccess *coredata.TrustCenterDocumentAccess var documentAccess *coredata.TrustCenterDocumentAccess
@@ -337,7 +337,7 @@ func (s TrustCenterAccessService) LoadDocumentAccess(
func (s TrustCenterAccessService) LoadReportAccess( func (s TrustCenterAccessService) LoadReportAccess(
ctx context.Context, ctx context.Context,
trustCenterID gid.GID, trustCenterID gid.GID,
email string, email mail.Addr,
reportID gid.GID, reportID gid.GID,
) (*coredata.TrustCenterDocumentAccess, error) { ) (*coredata.TrustCenterDocumentAccess, error) {
var reportAccess *coredata.TrustCenterDocumentAccess var reportAccess *coredata.TrustCenterDocumentAccess
@@ -372,7 +372,7 @@ func (s TrustCenterAccessService) LoadReportAccess(
func (s TrustCenterAccessService) LoadTrustCenterFileAccess( func (s TrustCenterAccessService) LoadTrustCenterFileAccess(
ctx context.Context, ctx context.Context,
trustCenterID gid.GID, trustCenterID gid.GID,
email string, email mail.Addr,
trustCenterFileID gid.GID, trustCenterFileID gid.GID,
) (*coredata.TrustCenterDocumentAccess, error) { ) (*coredata.TrustCenterDocumentAccess, error) {
var fileAccess *coredata.TrustCenterDocumentAccess var fileAccess *coredata.TrustCenterDocumentAccess
@@ -407,7 +407,7 @@ func (s TrustCenterAccessService) LoadTrustCenterFileAccess(
func (s *TrustCenterAccessService) GrantByIDs( func (s *TrustCenterAccessService) GrantByIDs(
ctx context.Context, ctx context.Context,
organizationID gid.GID, organizationID gid.GID,
email string, email mail.Addr,
documentIDs []gid.GID, documentIDs []gid.GID,
reportIDs []gid.GID, reportIDs []gid.GID,
fileIDs []gid.GID, fileIDs []gid.GID,
@@ -533,7 +533,7 @@ func (s *TrustCenterAccessService) sendTrustCenterAccessEmail(
ctx context.Context, ctx context.Context,
tx pg.Conn, tx pg.Conn,
name string, name string,
email string, email mail.Addr,
companyName string, companyName string,
accessURL string, accessURL string,
) error { ) error {
@@ -565,7 +565,7 @@ func (s *TrustCenterAccessService) sendTrustCenterAccessEmail(
func (s *TrustCenterAccessService) RejectOrRevokeByIDs( func (s *TrustCenterAccessService) RejectOrRevokeByIDs(
ctx context.Context, ctx context.Context,
organizationID gid.GID, organizationID gid.GID,
email string, email mail.Addr,
documentIDs []gid.GID, documentIDs []gid.GID,
reportIDs []gid.GID, reportIDs []gid.GID,
fileIDs []gid.GID, fileIDs []gid.GID,

View File

@@ -21,11 +21,12 @@ import (
"github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3"
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/page"
"go.probo.inc/probo/pkg/watermarkpdf" "go.probo.inc/probo/pkg/watermarkpdf"
"go.gearno.de/kit/pg"
) )
type TrustCenterFileService struct { type TrustCenterFileService struct {
@@ -86,7 +87,7 @@ func (s *TrustCenterFileService) ListForOrganizationId(
func (s *TrustCenterFileService) ExportFile( func (s *TrustCenterFileService) ExportFile(
ctx context.Context, ctx context.Context,
trustCenterFileID gid.GID, trustCenterFileID gid.GID,
email string, email mail.Addr,
) ([]byte, error) { ) ([]byte, error) {
pdfData, err := s.exportFileData(ctx, trustCenterFileID) pdfData, err := s.exportFileData(ctx, trustCenterFileID)
if err != nil { if err != nil {

View File

@@ -25,7 +25,7 @@ func BenchmarkValidate_SingleField(b *testing.B) {
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
v := New() v := New()
v.Check(&email, "email", Required(), Email()) v.Check(&email, "email", Required(), NotEmpty())
} }
} }
@@ -37,7 +37,7 @@ func BenchmarkValidate_MultipleFields(b *testing.B) {
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
v := New() v := New()
v.Check(&email, "email", Required(), Email()) v.Check(&email, "email", Required(), NotEmpty())
v.Check(&password, "password", Required(), MinLen(8)) v.Check(&password, "password", Required(), MinLen(8))
v.Check(&age, "age", Min(18), Max(120)) v.Check(&age, "age", Min(18), Max(120))
} }
@@ -108,16 +108,6 @@ func BenchmarkValidate_ArrayValidation(b *testing.B) {
} }
} }
func BenchmarkEmail(b *testing.B) {
email := "test@example.com"
validator := Email()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = validator(&email)
}
}
func BenchmarkURL(b *testing.B) { func BenchmarkURL(b *testing.B) {
urlStr := "https://example.com" urlStr := "https://example.com"
validator := URL() validator := URL()
@@ -214,7 +204,7 @@ func BenchmarkValidate_WithErrors(b *testing.B) {
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
v := New() v := New()
v.Check(&email, "email", Required(), Email()) v.Check(&email, "email", Required(), NotEmpty())
if !v.HasErrors() { if !v.HasErrors() {
b.Fatal("expected validation error") b.Fatal("expected validation error")
} }
@@ -256,7 +246,7 @@ func BenchmarkValidate_ComplexForm(b *testing.B) {
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
v := New() v := New()
v.Check(&user.Email, "email", Required(), Email()) v.Check(&user.Email, "email", Required(), NotEmpty())
v.Check(&user.Name, "name", Required(), MinLen(2)) v.Check(&user.Name, "name", Required(), MinLen(2))
v.Check(&user.Age, "age", Min(18), Max(120)) v.Check(&user.Age, "age", Min(18), Max(120))
v.Check(user.Website, "website", URL()) v.Check(user.Website, "website", URL())

View File

@@ -19,13 +19,15 @@ import (
"testing" "testing"
"go.gearno.de/x/ref" "go.gearno.de/x/ref"
"go.probo.inc/probo/pkg/mail"
) )
func TestValidator_Validate(t *testing.T) { func TestValidator_Validate(t *testing.T) {
t.Run("single field validation", func(t *testing.T) { t.Run("single field validation", func(t *testing.T) {
v := New() v := New()
email := "test@example.com" email := mail.Addr("test@example.com")
v.Check(&email, "email", Required(), Email())
v.Check(&email, "email", Required(), NotEmpty())
if v.HasErrors() { if v.HasErrors() {
t.Errorf("expected no errors, got: %v", v.Errors()) t.Errorf("expected no errors, got: %v", v.Errors())
@@ -34,10 +36,10 @@ func TestValidator_Validate(t *testing.T) {
t.Run("multiple field validations", func(t *testing.T) { t.Run("multiple field validations", func(t *testing.T) {
v := New() v := New()
email := "" email := mail.Nil
password := "123" password := "123"
v.Check(&email, "email", Required(), Email()) v.Check(email, "email", NotEmpty())
v.Check(&password, "password", Required(), MinLen(8)) v.Check(&password, "password", Required(), MinLen(8))
if !v.HasErrors() { if !v.HasErrors() {
@@ -69,8 +71,8 @@ func TestValidator_CheckNested(t *testing.T) {
v := New() v := New()
v.CheckNested("user", func(nv *Validator) { v.CheckNested("user", func(nv *Validator) {
email := "invalid" email := mail.Nil
nv.Check(&email, "email", Email()) nv.Check(&email, "email", NotEmpty())
nv.CheckNested("address", func(av *Validator) { nv.CheckNested("address", func(av *Validator) {
city := "" city := ""
@@ -186,7 +188,7 @@ func TestOptionalFieldExample(t *testing.T) {
v := New() v := New()
v.Check(&req.Email, "email", Required(), Email()) v.Check(&req.Email, "email", Required(), NotEmpty())
v.Check(&req.Name, "name", Required(), MinLen(2)) v.Check(&req.Name, "name", Required(), MinLen(2))
v.Check(req.Website, "website", URL()) v.Check(req.Website, "website", URL())
v.Check(req.PhoneNumber, "phoneNumber", MinLen(10)) v.Check(req.PhoneNumber, "phoneNumber", MinLen(10))
@@ -232,7 +234,7 @@ func TestRealWorldExample(t *testing.T) {
} }
user := User{ user := User{
Email: "invalid-email", Email: "",
Password: "123", Password: "123",
Age: 15, Age: 15,
Website: ref.Ref("not-a-url"), Website: ref.Ref("not-a-url"),
@@ -245,7 +247,7 @@ func TestRealWorldExample(t *testing.T) {
v := New() v := New()
// Validate user fields // Validate user fields
v.Check(&user.Email, "email", Required(), Email()) v.Check(&user.Email, "email", Required(), NotEmpty())
v.Check(&user.Password, "password", Required(), MinLen(8)) v.Check(&user.Password, "password", Required(), MinLen(8))
v.Check(&user.Age, "age", Min(18), Max(120)) v.Check(&user.Age, "age", Min(18), Max(120))
v.Check(user.Website, "website", URL()) v.Check(user.Website, "website", URL())
@@ -262,7 +264,7 @@ func TestRealWorldExample(t *testing.T) {
errors := v.Errors() errors := v.Errors()
expectedErrors := map[string]ErrorCode{ expectedErrors := map[string]ErrorCode{
"email": ErrorCodeInvalidEmail, "email": ErrorCodeRequired,
"password": ErrorCodeTooShort, "password": ErrorCodeTooShort,
"age": ErrorCodeOutOfRange, "age": ErrorCodeOutOfRange,
"website": ErrorCodeInvalidURL, "website": ErrorCodeInvalidURL,
@@ -373,10 +375,10 @@ func TestDuplicateValidators(t *testing.T) {
} }
}) })
t.Run("duplicate Email creates two errors", func(t *testing.T) { t.Run("duplicate NotEmpty creates two errors", func(t *testing.T) {
v := New() v := New()
email := "invalid" email := mail.Nil
v.Check(&email, "email", Email(), Email()) v.Check(&email, "email", NotEmpty(), NotEmpty())
errors := v.Errors() errors := v.Errors()
if len(errors) != 2 { if len(errors) != 2 {
@@ -405,22 +407,23 @@ func TestDuplicateValidators(t *testing.T) {
func TestStandardErrorPattern(t *testing.T) { func TestStandardErrorPattern(t *testing.T) {
// Simulates a typical validation function // Simulates a typical validation function
validateUser := func(email, password string) error { validateUser := func(email mail.Addr, password string) error {
v := New() v := New()
v.Check(&email, "email", Required(), Email()) v.Check(&email, "email", Required(), NotEmpty())
v.Check(&password, "password", Required(), MinLen(8)) v.Check(&password, "password", Required(), MinLen(8))
return v.Error() return v.Error()
} }
t.Run("valid data returns nil", func(t *testing.T) { t.Run("valid data returns nil", func(t *testing.T) {
err := validateUser("user@example.com", "password123") email := mail.Addr("user@example.com")
if err != nil {
if err := validateUser(email, "password123"); err != nil {
t.Errorf("expected nil, got: %v", err) t.Errorf("expected nil, got: %v", err)
} }
}) })
t.Run("invalid data returns ValidationErrors as error", func(t *testing.T) { t.Run("invalid data returns ValidationErrors as error", func(t *testing.T) {
err := validateUser("", "123") err := validateUser(mail.Nil, "123")
if err == nil { if err == nil {
t.Fatal("expected validation errors") t.Fatal("expected validation errors")
} }

View File

@@ -17,6 +17,8 @@ package validator
import ( import (
"reflect" "reflect"
"strings" "strings"
"go.probo.inc/probo/pkg/mail"
) )
// Required validates that a field has a value. // Required validates that a field has a value.
@@ -59,6 +61,10 @@ func NotEmpty() ValidatorFunc {
if strings.TrimSpace(v) == "" { if strings.TrimSpace(v) == "" {
return newValidationError(ErrorCodeRequired, "field cannot be empty") return newValidationError(ErrorCodeRequired, "field cannot be empty")
} }
case mail.Addr:
if v == mail.Nil {
return newValidationError(ErrorCodeRequired, "field cannot be empty")
}
default: default:
rv := reflect.ValueOf(actualValue) rv := reflect.ValueOf(actualValue)
if rv.Kind() == reflect.Slice && rv.Len() == 0 { if rv.Kind() == reflect.Slice && rv.Len() == 0 {

View File

@@ -23,36 +23,10 @@ import (
var ( var (
emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$`) emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$`)
gidRegex = regexp.MustCompile(`^gid://[a-zA-Z0-9\-_]+/[a-zA-Z0-9\-_]+/[a-zA-Z0-9\-_]+$`)
uuidRegex = regexp.MustCompile(`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`) uuidRegex = regexp.MustCompile(`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`)
domainRegex = regexp.MustCompile(`^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)*[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$`) domainRegex = regexp.MustCompile(`^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)*[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$`)
) )
// Email validates that a string is a valid email address.
func Email() ValidatorFunc {
return func(value any) *ValidationError {
actualValue, isNil := dereferenceValue(value)
if isNil {
return nil
}
str, ok := actualValue.(string)
if !ok {
return newValidationError(ErrorCodeInvalidEmail, "value must be a string")
}
if str == "" {
return nil
}
if !emailRegex.MatchString(str) {
return newValidationError(ErrorCodeInvalidEmail, "invalid email address")
}
return nil
}
}
// URL validates that a string is a valid URL with http or https scheme. // URL validates that a string is a valid URL with http or https scheme.
func URL() ValidatorFunc { func URL() ValidatorFunc {
return func(value any) *ValidationError { return func(value any) *ValidationError {

View File

@@ -21,35 +21,6 @@ import (
"go.probo.inc/probo/pkg/gid" "go.probo.inc/probo/pkg/gid"
) )
func TestEmail(t *testing.T) {
tests := []struct {
name string
value any
wantError bool
}{
{"valid email", "test@example.com", false},
{"valid email with plus", "test+tag@example.com", false},
{"invalid email no @", "testexample.com", true},
{"invalid email no domain", "test@", true},
{"invalid email no TLD", "test@example", true},
{"empty string", "", false}, // Empty is allowed, use Required() to enforce
{"nil pointer", (*string)(nil), false}, // Skip validation
{"non-string", 123, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := Email()(tt.value)
if (err != nil) != tt.wantError {
t.Errorf("Email() error = %v, wantError %v", err, tt.wantError)
}
if err != nil && err.Code != ErrorCodeInvalidEmail {
t.Errorf("Expected error code %s, got %s", ErrorCodeInvalidEmail, err.Code)
}
})
}
}
func TestURL(t *testing.T) { func TestURL(t *testing.T) {
tests := []struct { tests := []struct {
name string name string

View File

@@ -25,6 +25,7 @@ import (
"github.com/pdfcpu/pdfcpu/pkg/api" "github.com/pdfcpu/pdfcpu/pkg/api"
"github.com/pdfcpu/pdfcpu/pkg/pdfcpu/types" "github.com/pdfcpu/pdfcpu/pkg/pdfcpu/types"
"go.probo.inc/probo/pkg/mail"
"golang.org/x/image/font" "golang.org/x/image/font"
"golang.org/x/image/font/gofont/goregular" "golang.org/x/image/font/gofont/goregular"
"golang.org/x/image/font/opentype" "golang.org/x/image/font/opentype"
@@ -46,12 +47,12 @@ var (
fontColor = color.RGBA{0, 0, 0, 255} fontColor = color.RGBA{0, 0, 0, 255}
) )
func AddConfidentialWithTimestamp(pdfData []byte, email string) ([]byte, error) { func AddConfidentialWithTimestamp(pdfData []byte, email mail.Addr) ([]byte, error) {
reader := bytes.NewReader(pdfData) reader := bytes.NewReader(pdfData)
watermarkLines := []string{ watermarkLines := []string{
"Confidential", "Confidential",
email, email.String(),
time.Now().Format("2006-01-02"), time.Now().Format("2006-01-02"),
} }