Send mailing list emails

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2026-03-05 18:05:41 +01:00
parent aa01c40184
commit 85ec106cd6
67 changed files with 7615 additions and 160 deletions

View File

@@ -90,7 +90,6 @@ func NewMux(
r.Use(authn.NewSessionMiddleware(iamSvc, cookieConfig))
graphqlHandler := NewGraphQLHandler(iamSvc, trustSvc, esignSvc, mailmanSvc, logger, baseURL, cookieConfig, tokenSecret)
r.Handle("/graphql", graphqlHandler)
return r
@@ -99,4 +98,3 @@ func NewMux(
func (r *Resolver) TrustService(ctx context.Context, tenantID gid.TenantID) *trust.TenantService {
return r.trust.WithTenant(tenantID)
}

View File

@@ -131,6 +131,23 @@ type ComplianceFrameworkEdge
node: ComplianceFramework!
}
type MailingListUpdate implements Node @nda {
id: ID!
title: String!
body: String!
updatedAt: Datetime!
}
type MailingListUpdateConnection @nda {
edges: [MailingListUpdateEdge!]!
pageInfo: PageInfo!
}
type MailingListUpdateEdge @nda {
cursor: CursorKey!
node: MailingListUpdate!
}
enum CountryCode
@goModel(model: "go.probo.inc/probo/pkg/coredata.CountryCode") {
AD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAD")
@@ -581,6 +598,13 @@ type TrustCenter implements Node {
last: Int
before: CursorKey
): ComplianceExternalURLConnection! @goField(forceResolver: true)
updates(
first: Int
after: CursorKey
last: Int
before: CursorKey
): MailingListUpdateConnection! @goField(forceResolver: true)
}
type ComplianceExternalURL implements Node {
@@ -926,6 +950,7 @@ type MailingListSubscriber implements Node
model: "go.probo.inc/probo/pkg/server/api/trust/v1/types.MailingListSubscriber"
) {
id: ID!
fullName: String!
email: EmailAddr!
status: MailingListSubscriberStatus!
createdAt: Datetime!

View File

@@ -175,11 +175,29 @@ type ComplexityRoot struct {
MailingListSubscriber struct {
CreatedAt func(childComplexity int) int
Email func(childComplexity int) int
FullName func(childComplexity int) int
ID func(childComplexity int) int
Status func(childComplexity int) int
UpdatedAt func(childComplexity int) int
}
MailingListUpdate struct {
Body func(childComplexity int) int
ID func(childComplexity int) int
Title func(childComplexity int) int
UpdatedAt func(childComplexity int) int
}
MailingListUpdateConnection struct {
Edges func(childComplexity int) int
PageInfo func(childComplexity int) int
}
MailingListUpdateEdge struct {
Cursor func(childComplexity int) int
Node func(childComplexity int) int
}
Mutation struct {
AcceptElectronicSignature func(childComplexity int, input types.AcceptElectronicSignatureInput) int
ExportDocumentPDF func(childComplexity int, input types.ExportDocumentPDFInput) int
@@ -275,6 +293,7 @@ type ComplexityRoot struct {
References func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey) int
Slug func(childComplexity int) int
TrustCenterFiles func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey) int
Updates func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey) int
Vendors func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey) int
ViewerSubscription func(childComplexity int) int
}
@@ -417,6 +436,7 @@ type TrustCenterResolver interface {
TrustCenterFiles(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.TrustCenterFileConnection, error)
ComplianceFrameworks(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.ComplianceFrameworkConnection, error)
ExternalUrls(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.ComplianceExternalURLConnection, error)
Updates(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.MailingListUpdateConnection, error)
}
type TrustCenterFileResolver interface {
IsUserAuthorized(ctx context.Context, obj *types.TrustCenterFile) (bool, error)
@@ -805,6 +825,12 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
}
return e.ComplexityRoot.MailingListSubscriber.Email(childComplexity), true
case "MailingListSubscriber.fullName":
if e.ComplexityRoot.MailingListSubscriber.FullName == nil {
break
}
return e.ComplexityRoot.MailingListSubscriber.FullName(childComplexity), true
case "MailingListSubscriber.id":
if e.ComplexityRoot.MailingListSubscriber.ID == nil {
break
@@ -824,6 +850,57 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
return e.ComplexityRoot.MailingListSubscriber.UpdatedAt(childComplexity), true
case "MailingListUpdate.body":
if e.ComplexityRoot.MailingListUpdate.Body == nil {
break
}
return e.ComplexityRoot.MailingListUpdate.Body(childComplexity), true
case "MailingListUpdate.id":
if e.ComplexityRoot.MailingListUpdate.ID == nil {
break
}
return e.ComplexityRoot.MailingListUpdate.ID(childComplexity), true
case "MailingListUpdate.title":
if e.ComplexityRoot.MailingListUpdate.Title == nil {
break
}
return e.ComplexityRoot.MailingListUpdate.Title(childComplexity), true
case "MailingListUpdate.updatedAt":
if e.ComplexityRoot.MailingListUpdate.UpdatedAt == nil {
break
}
return e.ComplexityRoot.MailingListUpdate.UpdatedAt(childComplexity), true
case "MailingListUpdateConnection.edges":
if e.ComplexityRoot.MailingListUpdateConnection.Edges == nil {
break
}
return e.ComplexityRoot.MailingListUpdateConnection.Edges(childComplexity), true
case "MailingListUpdateConnection.pageInfo":
if e.ComplexityRoot.MailingListUpdateConnection.PageInfo == nil {
break
}
return e.ComplexityRoot.MailingListUpdateConnection.PageInfo(childComplexity), true
case "MailingListUpdateEdge.cursor":
if e.ComplexityRoot.MailingListUpdateEdge.Cursor == nil {
break
}
return e.ComplexityRoot.MailingListUpdateEdge.Cursor(childComplexity), true
case "MailingListUpdateEdge.node":
if e.ComplexityRoot.MailingListUpdateEdge.Node == nil {
break
}
return e.ComplexityRoot.MailingListUpdateEdge.Node(childComplexity), true
case "Mutation.acceptElectronicSignature":
if e.ComplexityRoot.Mutation.AcceptElectronicSignature == nil {
break
@@ -1258,6 +1335,17 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
}
return e.ComplexityRoot.TrustCenter.TrustCenterFiles(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey)), true
case "TrustCenter.updates":
if e.ComplexityRoot.TrustCenter.Updates == nil {
break
}
args, err := ec.field_TrustCenter_updates_args(ctx, rawArgs)
if err != nil {
return 0, false
}
return e.ComplexityRoot.TrustCenter.Updates(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey)), true
case "TrustCenter.vendors":
if e.ComplexityRoot.TrustCenter.Vendors == nil {
break
@@ -1744,6 +1832,23 @@ type ComplianceFrameworkEdge
node: ComplianceFramework!
}
type MailingListUpdate implements Node @nda {
id: ID!
title: String!
body: String!
updatedAt: Datetime!
}
type MailingListUpdateConnection @nda {
edges: [MailingListUpdateEdge!]!
pageInfo: PageInfo!
}
type MailingListUpdateEdge @nda {
cursor: CursorKey!
node: MailingListUpdate!
}
enum CountryCode
@goModel(model: "go.probo.inc/probo/pkg/coredata.CountryCode") {
AD @goEnum(value: "go.probo.inc/probo/pkg/coredata.CountryCodeAD")
@@ -2194,6 +2299,13 @@ type TrustCenter implements Node {
last: Int
before: CursorKey
): ComplianceExternalURLConnection! @goField(forceResolver: true)
updates(
first: Int
after: CursorKey
last: Int
before: CursorKey
): MailingListUpdateConnection! @goField(forceResolver: true)
}
type ComplianceExternalURL implements Node {
@@ -2539,6 +2651,7 @@ type MailingListSubscriber implements Node
model: "go.probo.inc/probo/pkg/server/api/trust/v1/types.MailingListSubscriber"
) {
id: ID!
fullName: String!
email: EmailAddr!
status: MailingListSubscriberStatus!
createdAt: Datetime!
@@ -2901,6 +3014,32 @@ func (ec *executionContext) field_TrustCenter_trustCenterFiles_args(ctx context.
return args, nil
}
func (ec *executionContext) field_TrustCenter_updates_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
var err error
args := map[string]any{}
arg0, err := graphql.ProcessArgField(ctx, rawArgs, "first", ec.unmarshalOInt2ᚖint)
if err != nil {
return nil, err
}
args["first"] = arg0
arg1, err := graphql.ProcessArgField(ctx, rawArgs, "after", ec.unmarshalOCursorKey2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋpageᚐCursorKey)
if err != nil {
return nil, err
}
args["after"] = arg1
arg2, err := graphql.ProcessArgField(ctx, rawArgs, "last", ec.unmarshalOInt2ᚖint)
if err != nil {
return nil, err
}
args["last"] = arg2
arg3, err := graphql.ProcessArgField(ctx, rawArgs, "before", ec.unmarshalOCursorKey2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋpageᚐCursorKey)
if err != nil {
return nil, err
}
args["before"] = arg3
return args, nil
}
func (ec *executionContext) field_TrustCenter_vendors_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
var err error
args := map[string]any{}
@@ -4850,6 +4989,35 @@ func (ec *executionContext) fieldContext_MailingListSubscriber_id(_ context.Cont
return fc, nil
}
func (ec *executionContext) _MailingListSubscriber_fullName(ctx context.Context, field graphql.CollectedField, obj *types.MailingListSubscriber) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_MailingListSubscriber_fullName,
func(ctx context.Context) (any, error) {
return obj.FullName, nil
},
nil,
ec.marshalNString2string,
true,
true,
)
}
func (ec *executionContext) fieldContext_MailingListSubscriber_fullName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "MailingListSubscriber",
Field: field,
IsMethod: false,
IsResolver: false,
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 fc, nil
}
func (ec *executionContext) _MailingListSubscriber_email(ctx context.Context, field graphql.CollectedField, obj *types.MailingListSubscriber) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
@@ -4966,6 +5134,290 @@ func (ec *executionContext) fieldContext_MailingListSubscriber_updatedAt(_ conte
return fc, nil
}
func (ec *executionContext) _MailingListUpdate_id(ctx context.Context, field graphql.CollectedField, obj *types.MailingListUpdate) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_MailingListUpdate_id,
func(ctx context.Context) (any, error) {
return obj.ID, nil
},
nil,
ec.marshalNID2goᚗproboᚗincᚋproboᚋpkgᚋgidᚐGID,
true,
true,
)
}
func (ec *executionContext) fieldContext_MailingListUpdate_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "MailingListUpdate",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type ID does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _MailingListUpdate_title(ctx context.Context, field graphql.CollectedField, obj *types.MailingListUpdate) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_MailingListUpdate_title,
func(ctx context.Context) (any, error) {
return obj.Title, nil
},
nil,
ec.marshalNString2string,
true,
true,
)
}
func (ec *executionContext) fieldContext_MailingListUpdate_title(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "MailingListUpdate",
Field: field,
IsMethod: false,
IsResolver: false,
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 fc, nil
}
func (ec *executionContext) _MailingListUpdate_body(ctx context.Context, field graphql.CollectedField, obj *types.MailingListUpdate) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_MailingListUpdate_body,
func(ctx context.Context) (any, error) {
return obj.Body, nil
},
nil,
ec.marshalNString2string,
true,
true,
)
}
func (ec *executionContext) fieldContext_MailingListUpdate_body(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "MailingListUpdate",
Field: field,
IsMethod: false,
IsResolver: false,
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 fc, nil
}
func (ec *executionContext) _MailingListUpdate_updatedAt(ctx context.Context, field graphql.CollectedField, obj *types.MailingListUpdate) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_MailingListUpdate_updatedAt,
func(ctx context.Context) (any, error) {
return obj.UpdatedAt, nil
},
nil,
ec.marshalNDatetime2timeᚐTime,
true,
true,
)
}
func (ec *executionContext) fieldContext_MailingListUpdate_updatedAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "MailingListUpdate",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type Datetime does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _MailingListUpdateConnection_edges(ctx context.Context, field graphql.CollectedField, obj *types.MailingListUpdateConnection) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_MailingListUpdateConnection_edges,
func(ctx context.Context) (any, error) {
return obj.Edges, nil
},
func(ctx context.Context, next graphql.Resolver) graphql.Resolver {
directive0 := next
directive1 := func(ctx context.Context) (any, error) {
if ec.Directives.Nda == nil {
var zeroVal []*types.MailingListUpdateEdge
return zeroVal, errors.New("directive nda is not implemented")
}
return ec.Directives.Nda(ctx, obj, directive0)
}
next = directive1
return next
},
ec.marshalNMailingListUpdateEdge2ᚕᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐMailingListUpdateEdgeᚄ,
true,
true,
)
}
func (ec *executionContext) fieldContext_MailingListUpdateConnection_edges(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "MailingListUpdateConnection",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
switch field.Name {
case "cursor":
return ec.fieldContext_MailingListUpdateEdge_cursor(ctx, field)
case "node":
return ec.fieldContext_MailingListUpdateEdge_node(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type MailingListUpdateEdge", field.Name)
},
}
return fc, nil
}
func (ec *executionContext) _MailingListUpdateConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *types.MailingListUpdateConnection) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_MailingListUpdateConnection_pageInfo,
func(ctx context.Context) (any, error) {
return obj.PageInfo, nil
},
nil,
ec.marshalNPageInfo2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐPageInfo,
true,
true,
)
}
func (ec *executionContext) fieldContext_MailingListUpdateConnection_pageInfo(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "MailingListUpdateConnection",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
switch field.Name {
case "hasNextPage":
return ec.fieldContext_PageInfo_hasNextPage(ctx, field)
case "hasPreviousPage":
return ec.fieldContext_PageInfo_hasPreviousPage(ctx, field)
case "startCursor":
return ec.fieldContext_PageInfo_startCursor(ctx, field)
case "endCursor":
return ec.fieldContext_PageInfo_endCursor(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type PageInfo", field.Name)
},
}
return fc, nil
}
func (ec *executionContext) _MailingListUpdateEdge_cursor(ctx context.Context, field graphql.CollectedField, obj *types.MailingListUpdateEdge) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_MailingListUpdateEdge_cursor,
func(ctx context.Context) (any, error) {
return obj.Cursor, nil
},
nil,
ec.marshalNCursorKey2goᚗproboᚗincᚋproboᚋpkgᚋpageᚐCursorKey,
true,
true,
)
}
func (ec *executionContext) fieldContext_MailingListUpdateEdge_cursor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "MailingListUpdateEdge",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type CursorKey does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _MailingListUpdateEdge_node(ctx context.Context, field graphql.CollectedField, obj *types.MailingListUpdateEdge) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_MailingListUpdateEdge_node,
func(ctx context.Context) (any, error) {
return obj.Node, nil
},
func(ctx context.Context, next graphql.Resolver) graphql.Resolver {
directive0 := next
directive1 := func(ctx context.Context) (any, error) {
if ec.Directives.Nda == nil {
var zeroVal *types.MailingListUpdate
return zeroVal, errors.New("directive nda is not implemented")
}
return ec.Directives.Nda(ctx, obj, directive0)
}
next = directive1
return next
},
ec.marshalNMailingListUpdate2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐMailingListUpdate,
true,
true,
)
}
func (ec *executionContext) fieldContext_MailingListUpdateEdge_node(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "MailingListUpdateEdge",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
switch field.Name {
case "id":
return ec.fieldContext_MailingListUpdate_id(ctx, field)
case "title":
return ec.fieldContext_MailingListUpdate_title(ctx, field)
case "body":
return ec.fieldContext_MailingListUpdate_body(ctx, field)
case "updatedAt":
return ec.fieldContext_MailingListUpdate_updatedAt(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type MailingListUpdate", field.Name)
},
}
return fc, nil
}
func (ec *executionContext) _Mutation_sendMagicLink(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
@@ -6423,6 +6875,8 @@ func (ec *executionContext) fieldContext_Query_currentTrustCenter(_ context.Cont
return ec.fieldContext_TrustCenter_complianceFrameworks(ctx, field)
case "externalUrls":
return ec.fieldContext_TrustCenter_externalUrls(ctx, field)
case "updates":
return ec.fieldContext_TrustCenter_updates(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type TrustCenter", field.Name)
},
@@ -6945,6 +7399,8 @@ func (ec *executionContext) fieldContext_SubscribeToMailingListPayload_subscript
switch field.Name {
case "id":
return ec.fieldContext_MailingListSubscriber_id(ctx, field)
case "fullName":
return ec.fieldContext_MailingListSubscriber_fullName(ctx, field)
case "email":
return ec.fieldContext_MailingListSubscriber_email(ctx, field)
case "status":
@@ -7168,6 +7624,8 @@ func (ec *executionContext) fieldContext_TrustCenter_viewerSubscription(_ contex
switch field.Name {
case "id":
return ec.fieldContext_MailingListSubscriber_id(ctx, field)
case "fullName":
return ec.fieldContext_MailingListSubscriber_fullName(ctx, field)
case "email":
return ec.fieldContext_MailingListSubscriber_email(ctx, field)
case "status":
@@ -7624,6 +8082,66 @@ func (ec *executionContext) fieldContext_TrustCenter_externalUrls(ctx context.Co
return fc, nil
}
func (ec *executionContext) _TrustCenter_updates(ctx context.Context, field graphql.CollectedField, obj *types.TrustCenter) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_TrustCenter_updates,
func(ctx context.Context) (any, error) {
fc := graphql.GetFieldContext(ctx)
return ec.Resolvers.TrustCenter().Updates(ctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey))
},
func(ctx context.Context, next graphql.Resolver) graphql.Resolver {
directive0 := next
directive1 := func(ctx context.Context) (any, error) {
if ec.Directives.Nda == nil {
var zeroVal *types.MailingListUpdateConnection
return zeroVal, errors.New("directive nda is not implemented")
}
return ec.Directives.Nda(ctx, obj, directive0)
}
next = directive1
return next
},
ec.marshalNMailingListUpdateConnection2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐMailingListUpdateConnection,
true,
true,
)
}
func (ec *executionContext) fieldContext_TrustCenter_updates(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "TrustCenter",
Field: field,
IsMethod: true,
IsResolver: true,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
switch field.Name {
case "edges":
return ec.fieldContext_MailingListUpdateConnection_edges(ctx, field)
case "pageInfo":
return ec.fieldContext_MailingListUpdateConnection_pageInfo(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type MailingListUpdateConnection", field.Name)
},
}
defer func() {
if r := recover(); r != nil {
err = ec.Recover(ctx, r)
ec.Error(ctx, err)
}
}()
ctx = graphql.WithFieldContext(ctx, fc)
if fc.Args, err = ec.field_TrustCenter_updates_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
ec.Error(ctx, err)
return fc, err
}
return fc, nil
}
func (ec *executionContext) _TrustCenterAccess_id(ctx context.Context, field graphql.CollectedField, obj *types.TrustCenterAccess) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
@@ -10701,6 +11219,13 @@ func (ec *executionContext) _Node(ctx context.Context, sel ast.SelectionSet, obj
return graphql.Null
}
return ec._Organization(ctx, sel, obj)
case types.MailingListUpdate:
return ec._MailingListUpdate(ctx, sel, &obj)
case *types.MailingListUpdate:
if obj == nil {
return graphql.Null
}
return ec._MailingListUpdate(ctx, sel, obj)
case types.MailingListSubscriber:
return ec._MailingListSubscriber(ctx, sel, &obj)
case *types.MailingListSubscriber:
@@ -11944,6 +12469,11 @@ func (ec *executionContext) _MailingListSubscriber(ctx context.Context, sel ast.
if out.Values[i] == graphql.Null {
out.Invalids++
}
case "fullName":
out.Values[i] = ec._MailingListSubscriber_fullName(ctx, field, obj)
if out.Values[i] == graphql.Null {
out.Invalids++
}
case "email":
out.Values[i] = ec._MailingListSubscriber_email(ctx, field, obj)
if out.Values[i] == graphql.Null {
@@ -11987,6 +12517,148 @@ func (ec *executionContext) _MailingListSubscriber(ctx context.Context, sel ast.
return out
}
var mailingListUpdateImplementors = []string{"MailingListUpdate", "Node"}
func (ec *executionContext) _MailingListUpdate(ctx context.Context, sel ast.SelectionSet, obj *types.MailingListUpdate) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, mailingListUpdateImplementors)
out := graphql.NewFieldSet(fields)
deferred := make(map[string]*graphql.FieldSet)
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("MailingListUpdate")
case "id":
out.Values[i] = ec._MailingListUpdate_id(ctx, field, obj)
if out.Values[i] == graphql.Null {
out.Invalids++
}
case "title":
out.Values[i] = ec._MailingListUpdate_title(ctx, field, obj)
if out.Values[i] == graphql.Null {
out.Invalids++
}
case "body":
out.Values[i] = ec._MailingListUpdate_body(ctx, field, obj)
if out.Values[i] == graphql.Null {
out.Invalids++
}
case "updatedAt":
out.Values[i] = ec._MailingListUpdate_updatedAt(ctx, field, obj)
if out.Values[i] == graphql.Null {
out.Invalids++
}
default:
panic("unknown field " + strconv.Quote(field.Name))
}
}
out.Dispatch(ctx)
if out.Invalids > 0 {
return graphql.Null
}
atomic.AddInt32(&ec.Deferred, int32(len(deferred)))
for label, dfs := range deferred {
ec.ProcessDeferredGroup(graphql.DeferredGroup{
Label: label,
Path: graphql.GetPath(ctx),
FieldSet: dfs,
Context: ctx,
})
}
return out
}
var mailingListUpdateConnectionImplementors = []string{"MailingListUpdateConnection"}
func (ec *executionContext) _MailingListUpdateConnection(ctx context.Context, sel ast.SelectionSet, obj *types.MailingListUpdateConnection) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, mailingListUpdateConnectionImplementors)
out := graphql.NewFieldSet(fields)
deferred := make(map[string]*graphql.FieldSet)
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("MailingListUpdateConnection")
case "edges":
out.Values[i] = ec._MailingListUpdateConnection_edges(ctx, field, obj)
if out.Values[i] == graphql.Null {
out.Invalids++
}
case "pageInfo":
out.Values[i] = ec._MailingListUpdateConnection_pageInfo(ctx, field, obj)
if out.Values[i] == graphql.Null {
out.Invalids++
}
default:
panic("unknown field " + strconv.Quote(field.Name))
}
}
out.Dispatch(ctx)
if out.Invalids > 0 {
return graphql.Null
}
atomic.AddInt32(&ec.Deferred, int32(len(deferred)))
for label, dfs := range deferred {
ec.ProcessDeferredGroup(graphql.DeferredGroup{
Label: label,
Path: graphql.GetPath(ctx),
FieldSet: dfs,
Context: ctx,
})
}
return out
}
var mailingListUpdateEdgeImplementors = []string{"MailingListUpdateEdge"}
func (ec *executionContext) _MailingListUpdateEdge(ctx context.Context, sel ast.SelectionSet, obj *types.MailingListUpdateEdge) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, mailingListUpdateEdgeImplementors)
out := graphql.NewFieldSet(fields)
deferred := make(map[string]*graphql.FieldSet)
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("MailingListUpdateEdge")
case "cursor":
out.Values[i] = ec._MailingListUpdateEdge_cursor(ctx, field, obj)
if out.Values[i] == graphql.Null {
out.Invalids++
}
case "node":
out.Values[i] = ec._MailingListUpdateEdge_node(ctx, field, obj)
if out.Values[i] == graphql.Null {
out.Invalids++
}
default:
panic("unknown field " + strconv.Quote(field.Name))
}
}
out.Dispatch(ctx)
if out.Invalids > 0 {
return graphql.Null
}
atomic.AddInt32(&ec.Deferred, int32(len(deferred)))
for label, dfs := range deferred {
ec.ProcessDeferredGroup(graphql.DeferredGroup{
Label: label,
Path: graphql.GetPath(ctx),
FieldSet: dfs,
Context: ctx,
})
}
return out
}
var mutationImplementors = []string{"Mutation"}
func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) graphql.Marshaler {
@@ -13285,6 +13957,42 @@ func (ec *executionContext) _TrustCenter(ctx context.Context, sel ast.SelectionS
continue
}
out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
case "updates":
field := field
innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._TrustCenter_updates(ctx, field, obj)
if res == graphql.Null {
atomic.AddUint32(&fs.Invalids, 1)
}
return res
}
if field.Deferrable != nil {
dfs, ok := deferred[field.Deferrable.Label]
di := 0
if ok {
dfs.AddField(field)
di = len(dfs.Values) - 1
} else {
dfs = graphql.NewFieldSet([]graphql.CollectedField{field})
deferred[field.Deferrable.Label] = dfs
}
dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler {
return innerFunc(ctx, dfs)
})
// don't run the out.Concurrently() call below
out.Values[i] = graphql.Null
continue
}
out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) })
default:
panic("unknown field " + strconv.Quote(field.Name))
@@ -15532,6 +16240,56 @@ var (
}
)
func (ec *executionContext) marshalNMailingListUpdate2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐMailingListUpdate(ctx context.Context, sel ast.SelectionSet, v *types.MailingListUpdate) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow")
}
return graphql.Null
}
return ec._MailingListUpdate(ctx, sel, v)
}
func (ec *executionContext) marshalNMailingListUpdateConnection2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐMailingListUpdateConnection(ctx context.Context, sel ast.SelectionSet, v types.MailingListUpdateConnection) graphql.Marshaler {
return ec._MailingListUpdateConnection(ctx, sel, &v)
}
func (ec *executionContext) marshalNMailingListUpdateConnection2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐMailingListUpdateConnection(ctx context.Context, sel ast.SelectionSet, v *types.MailingListUpdateConnection) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow")
}
return graphql.Null
}
return ec._MailingListUpdateConnection(ctx, sel, v)
}
func (ec *executionContext) marshalNMailingListUpdateEdge2ᚕᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐMailingListUpdateEdgeᚄ(ctx context.Context, sel ast.SelectionSet, v []*types.MailingListUpdateEdge) graphql.Marshaler {
ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler {
fc := graphql.GetFieldContext(ctx)
fc.Result = &v[i]
return ec.marshalNMailingListUpdateEdge2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐMailingListUpdateEdge(ctx, sel, v[i])
})
for _, e := range ret {
if e == graphql.Null {
return graphql.Null
}
}
return ret
}
func (ec *executionContext) marshalNMailingListUpdateEdge2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐMailingListUpdateEdge(ctx context.Context, sel ast.SelectionSet, v *types.MailingListUpdateEdge) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow")
}
return graphql.Null
}
return ec._MailingListUpdateEdge(ctx, sel, v)
}
func (ec *executionContext) marshalNNode2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋtrustᚋv1ᚋtypesᚐNode(ctx context.Context, sel ast.SelectionSet, v types.Node) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {

View File

@@ -24,6 +24,7 @@ import (
type MailingListSubscriber struct {
ID gid.GID `json:"id"`
FullName string `json:"fullName"`
Email mail.Addr `json:"email"`
Status coredata.MailingListSubscriberStatus `json:"status"`
CreatedAt time.Time `json:"createdAt"`
@@ -36,6 +37,7 @@ func (m MailingListSubscriber) GetID() gid.GID { return m.ID }
func NewMailingListSubscriber(s *coredata.MailingListSubscriber) *MailingListSubscriber {
return &MailingListSubscriber{
ID: s.ID,
FullName: s.FullName,
Email: s.Email,
Status: s.Status,
CreatedAt: s.CreatedAt,

View File

@@ -0,0 +1,50 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package types
import (
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/page"
)
func NewMailingListUpdate(mlu *coredata.MailingListUpdate) *MailingListUpdate {
return &MailingListUpdate{
ID: mlu.ID,
Title: mlu.Title,
Body: mlu.Body,
UpdatedAt: mlu.UpdatedAt,
}
}
func NewMailingListUpdateEdge(mlu *coredata.MailingListUpdate) *MailingListUpdateEdge {
return &MailingListUpdateEdge{
Cursor: mlu.CursorKey(coredata.MailingListUpdateOrderFieldUpdatedAt),
Node: NewMailingListUpdate(mlu),
}
}
func NewMailingListUpdateConnection(
p *page.Page[*coredata.MailingListUpdate, coredata.MailingListUpdateOrderField],
) *MailingListUpdateConnection {
edges := make([]*MailingListUpdateEdge, len(p.Data))
for i, mlu := range p.Data {
edges[i] = NewMailingListUpdateEdge(mlu)
}
return &MailingListUpdateConnection{
Edges: edges,
PageInfo: NewPageInfo(p),
}
}

View File

@@ -153,6 +153,26 @@ type Identity struct {
func (Identity) IsNode() {}
func (this Identity) GetID() gid.GID { return this.ID }
type MailingListUpdate struct {
ID gid.GID `json:"id"`
Title string `json:"title"`
Body string `json:"body"`
UpdatedAt time.Time `json:"updatedAt"`
}
func (MailingListUpdate) IsNode() {}
func (this MailingListUpdate) GetID() gid.GID { return this.ID }
type MailingListUpdateConnection struct {
Edges []*MailingListUpdateEdge `json:"edges"`
PageInfo *PageInfo `json:"pageInfo"`
}
type MailingListUpdateEdge struct {
Cursor page.CursorKey `json:"cursor"`
Node *MailingListUpdate `json:"node"`
}
type Mutation struct {
}
@@ -261,6 +281,7 @@ type TrustCenter struct {
TrustCenterFiles *TrustCenterFileConnection `json:"trustCenterFiles"`
ComplianceFrameworks *ComplianceFrameworkConnection `json:"complianceFrameworks"`
ExternalUrls *ComplianceExternalURLConnection `json:"externalUrls"`
Updates *MailingListUpdateConnection `json:"updates"`
}
func (TrustCenter) IsNode() {}

View File

@@ -704,18 +704,18 @@ func (r *mutationResolver) SubscribeToMailingList(ctx context.Context) (*types.S
identity := authn.IdentityFromContext(ctx)
subscriber, err := r.mailman.CreateSubscriber(ctx, *trustCenter.MailingListID, identity.EmailAddress, identity.FullName)
subscriber, err := r.mailman.CreateSubscriber(
ctx,
*trustCenter.MailingListID,
identity.EmailAddress,
identity.FullName,
)
if err != nil {
if errors.Is(err, mailman.ErrSubscriberAlreadyExist) {
subscriber, err = r.mailman.GetSubscriber(ctx, *trustCenter.MailingListID, identity.EmailAddress)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot get existing mailing list subscription", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
} else {
r.logger.ErrorCtx(ctx, "cannot subscribe to mailing list", log.Error(err))
return nil, gqlutils.Internal(ctx)
return nil, gqlutils.Conflictf(ctx, "already subscribed to this mailing list")
}
r.logger.ErrorCtx(ctx, "cannot subscribe to mailing list", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return &types.SubscribeToMailingListPayload{
@@ -738,10 +738,13 @@ func (r *mutationResolver) UnsubscribeFromMailingList(ctx context.Context) (*typ
return nil, gqlutils.Internal(ctx)
}
if subscriber == nil {
return nil, gqlutils.NotFoundf(ctx, "mailing list subscription not found")
return nil, gqlutils.NotFoundf(ctx, "not subscribed to this mailing list")
}
if err := r.mailman.DeleteSubscriber(ctx, subscriber.ID); err != nil {
if errors.Is(err, mailman.ErrSubscriberNotFound) {
return nil, gqlutils.NotFoundf(ctx, "not subscribed to this mailing list")
}
r.logger.ErrorCtx(ctx, "cannot unsubscribe from mailing list", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
@@ -1207,6 +1210,35 @@ func (r *trustCenterResolver) ExternalUrls(ctx context.Context, obj *types.Trust
return types.NewComplianceExternalURLConnection(result), nil
}
// Updates is the resolver for the updates field.
func (r *trustCenterResolver) Updates(ctx context.Context, obj *types.TrustCenter, first *int, after *page.CursorKey, last *int, before *page.CursorKey) (*types.MailingListUpdateConnection, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())
tc, err := trustService.TrustCenters.Get(ctx, obj.ID)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot load trust center", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
if tc.MailingListID == nil {
return &types.MailingListUpdateConnection{Edges: []*types.MailingListUpdateEdge{}, PageInfo: &types.PageInfo{}}, nil
}
pageOrderBy := page.OrderBy[coredata.MailingListUpdateOrderField]{
Field: coredata.MailingListUpdateOrderFieldUpdatedAt,
Direction: page.OrderDirectionDesc,
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
result, err := r.mailman.ListSentMailingListUpdates(ctx, *tc.MailingListID, cursor)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list mailing list updates", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewMailingListUpdateConnection(result), nil
}
// IsUserAuthorized is the resolver for the isUserAuthorized field.
func (r *trustCenterFileResolver) IsUserAuthorized(ctx context.Context, obj *types.TrustCenterFile) (bool, error) {
trustService := r.TrustService(ctx, obj.ID.TenantID())