@@ -124,6 +124,60 @@ LIMIT 1;
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Document) LoadByIDWithFilter(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
documentID gid.GID,
|
||||
filter *DocumentFilter,
|
||||
) error {
|
||||
q := `
|
||||
SELECT
|
||||
id,
|
||||
organization_id,
|
||||
owner_id,
|
||||
title,
|
||||
document_type,
|
||||
classification,
|
||||
current_published_version,
|
||||
trust_center_visibility,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
documents
|
||||
WHERE
|
||||
%s
|
||||
AND deleted_at IS NULL
|
||||
AND id = @document_id
|
||||
AND %s
|
||||
LIMIT 1;
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{"document_id": documentID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
maps.Copy(args, filter.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query documents: %w", err)
|
||||
}
|
||||
|
||||
document, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[Document])
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return &ErrDocumentNotFound{Identifier: documentID.String()}
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot collect document: %w", err)
|
||||
}
|
||||
|
||||
*p = document
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Documents) CountByOrganizationID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
@@ -400,28 +454,17 @@ func (p *Documents) CountByControlID(
|
||||
filter *DocumentFilter,
|
||||
) (int, error) {
|
||||
q := `
|
||||
WITH plcs AS (
|
||||
SELECT
|
||||
p.id,
|
||||
p.tenant_id,
|
||||
p.search_vector,
|
||||
p.trust_center_visibility,
|
||||
p.deleted_at
|
||||
FROM
|
||||
documents p
|
||||
INNER JOIN
|
||||
controls_documents cp ON p.id = cp.document_id
|
||||
WHERE
|
||||
cp.control_id = @control_id
|
||||
WITH scoped_documents AS (
|
||||
SELECT *
|
||||
FROM documents
|
||||
WHERE %s
|
||||
AND deleted_at IS NULL
|
||||
AND %s
|
||||
)
|
||||
SELECT
|
||||
COUNT(id)
|
||||
FROM
|
||||
plcs
|
||||
WHERE
|
||||
%s
|
||||
AND deleted_at IS NULL
|
||||
AND %s
|
||||
SELECT COUNT(scoped_documents.id)
|
||||
FROM scoped_documents
|
||||
INNER JOIN controls_documents cp ON scoped_documents.id = cp.document_id
|
||||
WHERE cp.control_id = @control_id
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment())
|
||||
@@ -448,46 +491,28 @@ func (p *Documents) LoadByControlID(
|
||||
filter *DocumentFilter,
|
||||
) error {
|
||||
q := `
|
||||
WITH plcs AS (
|
||||
SELECT
|
||||
p.id,
|
||||
p.tenant_id,
|
||||
p.search_vector,
|
||||
p.organization_id,
|
||||
p.owner_id,
|
||||
p.title,
|
||||
p.document_type,
|
||||
p.classification,
|
||||
p.current_published_version,
|
||||
p.trust_center_visibility,
|
||||
p.created_at,
|
||||
p.updated_at,
|
||||
p.deleted_at
|
||||
FROM
|
||||
documents p
|
||||
INNER JOIN
|
||||
controls_documents cp ON p.id = cp.document_id
|
||||
WHERE
|
||||
cp.control_id = @control_id
|
||||
WITH scoped_documents AS (
|
||||
SELECT *
|
||||
FROM documents
|
||||
WHERE %s
|
||||
AND deleted_at IS NULL
|
||||
AND %s
|
||||
AND %s
|
||||
)
|
||||
SELECT
|
||||
id,
|
||||
organization_id,
|
||||
owner_id,
|
||||
title,
|
||||
document_type,
|
||||
classification,
|
||||
current_published_version,
|
||||
trust_center_visibility,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
plcs
|
||||
WHERE
|
||||
%s
|
||||
AND deleted_at IS NULL
|
||||
AND %s
|
||||
AND %s
|
||||
scoped_documents.id,
|
||||
scoped_documents.organization_id,
|
||||
scoped_documents.owner_id,
|
||||
scoped_documents.title,
|
||||
scoped_documents.document_type,
|
||||
scoped_documents.classification,
|
||||
scoped_documents.current_published_version,
|
||||
scoped_documents.trust_center_visibility,
|
||||
scoped_documents.created_at,
|
||||
scoped_documents.updated_at
|
||||
FROM scoped_documents
|
||||
INNER JOIN controls_documents cp ON scoped_documents.id = cp.document_id
|
||||
WHERE cp.control_id = @control_id
|
||||
`
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment(), cursor.SQLFragment())
|
||||
|
||||
@@ -519,28 +544,17 @@ func (p *Documents) CountByRiskID(
|
||||
filter *DocumentFilter,
|
||||
) (int, error) {
|
||||
q := `
|
||||
WITH plcs AS (
|
||||
SELECT
|
||||
p.id,
|
||||
p.tenant_id,
|
||||
p.search_vector,
|
||||
p.trust_center_visibility,
|
||||
p.deleted_at
|
||||
FROM
|
||||
documents p
|
||||
INNER JOIN
|
||||
risks_documents rp ON p.id = rp.document_id
|
||||
WHERE
|
||||
rp.risk_id = @risk_id
|
||||
WITH scoped_documents AS (
|
||||
SELECT *
|
||||
FROM documents
|
||||
WHERE %s
|
||||
AND deleted_at IS NULL
|
||||
AND %s
|
||||
)
|
||||
SELECT
|
||||
COUNT(id)
|
||||
FROM
|
||||
plcs
|
||||
WHERE
|
||||
%s
|
||||
AND deleted_at IS NULL
|
||||
AND %s
|
||||
SELECT COUNT(scoped_documents.id)
|
||||
FROM scoped_documents
|
||||
INNER JOIN risks_documents rp ON scoped_documents.id = rp.document_id
|
||||
WHERE rp.risk_id = @risk_id
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment())
|
||||
@@ -567,46 +581,28 @@ func (p *Documents) LoadByRiskID(
|
||||
filter *DocumentFilter,
|
||||
) error {
|
||||
q := `
|
||||
WITH plcs AS (
|
||||
SELECT
|
||||
p.id,
|
||||
p.tenant_id,
|
||||
p.organization_id,
|
||||
p.owner_id,
|
||||
p.title,
|
||||
p.document_type,
|
||||
p.classification,
|
||||
p.current_published_version,
|
||||
p.trust_center_visibility,
|
||||
p.created_at,
|
||||
p.updated_at,
|
||||
p.search_vector,
|
||||
p.deleted_at
|
||||
FROM
|
||||
documents p
|
||||
INNER JOIN
|
||||
risks_documents rp ON p.id = rp.document_id
|
||||
WHERE
|
||||
rp.risk_id = @risk_id
|
||||
WITH scoped_documents AS (
|
||||
SELECT *
|
||||
FROM documents
|
||||
WHERE %s
|
||||
AND deleted_at IS NULL
|
||||
AND %s
|
||||
AND %s
|
||||
)
|
||||
SELECT
|
||||
id,
|
||||
organization_id,
|
||||
owner_id,
|
||||
title,
|
||||
document_type,
|
||||
classification,
|
||||
current_published_version,
|
||||
trust_center_visibility,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
plcs
|
||||
WHERE
|
||||
%s
|
||||
AND deleted_at IS NULL
|
||||
AND %s
|
||||
AND %s
|
||||
scoped_documents.id,
|
||||
scoped_documents.organization_id,
|
||||
scoped_documents.owner_id,
|
||||
scoped_documents.title,
|
||||
scoped_documents.document_type,
|
||||
scoped_documents.classification,
|
||||
scoped_documents.current_published_version,
|
||||
scoped_documents.trust_center_visibility,
|
||||
scoped_documents.created_at,
|
||||
scoped_documents.updated_at
|
||||
FROM scoped_documents
|
||||
INNER JOIN risks_documents rp ON scoped_documents.id = rp.document_id
|
||||
WHERE rp.risk_id = @risk_id
|
||||
`
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment(), cursor.SQLFragment())
|
||||
|
||||
@@ -653,3 +649,61 @@ UPDATE documents SET deleted_at = @deleted_at WHERE %s AND id = ANY(@document_id
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *Document) IsLastSignableVersionSignedByUserEmail(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
documentID gid.GID,
|
||||
userEmail string,
|
||||
) (bool, error) {
|
||||
q := `
|
||||
WITH last_signable_version AS (
|
||||
SELECT
|
||||
d.id AS document_id,
|
||||
d.tenant_id,
|
||||
dv.version_number,
|
||||
dvs.state
|
||||
FROM documents d
|
||||
INNER JOIN document_versions dv ON dv.document_id = d.id
|
||||
INNER JOIN document_version_signatures dvs ON dvs.document_version_id = dv.id
|
||||
INNER JOIN peoples p ON dvs.signed_by = p.id
|
||||
WHERE d.id = @document_id
|
||||
AND p.primary_email_address = @user_email
|
||||
AND dv.version_number = (
|
||||
SELECT MAX(dv2.version_number)
|
||||
FROM document_versions dv2
|
||||
INNER JOIN document_version_signatures dvs2 ON dvs2.document_version_id = dv2.id
|
||||
INNER JOIN peoples p2 ON dvs2.signed_by = p2.id
|
||||
WHERE dv2.document_id = d.id
|
||||
AND p2.primary_email_address = @user_email
|
||||
)
|
||||
)
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM last_signable_version
|
||||
WHERE %s
|
||||
AND state = 'SIGNED'
|
||||
) AS signed
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"document_id": documentID,
|
||||
"user_email": userEmail,
|
||||
}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("cannot query document signed status: %w", err)
|
||||
}
|
||||
|
||||
signed, err := pgx.CollectOneRow(rows, pgx.RowTo[bool])
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("cannot collect signed status: %w", err)
|
||||
}
|
||||
|
||||
return signed, nil
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ type (
|
||||
DocumentFilter struct {
|
||||
query *string
|
||||
trustCenterVisibilities []TrustCenterVisibility
|
||||
published *bool
|
||||
userEmail *string
|
||||
}
|
||||
)
|
||||
|
||||
@@ -40,7 +42,17 @@ func NewDocumentTrustCenterFilter() *DocumentFilter {
|
||||
}
|
||||
}
|
||||
|
||||
func (f *DocumentFilter) SQLArguments() pgx.StrictNamedArgs {
|
||||
func (f *DocumentFilter) WithPublished(published *bool) *DocumentFilter {
|
||||
f.published = published
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *DocumentFilter) WithUserEmail(userEmail *string) *DocumentFilter {
|
||||
f.userEmail = userEmail
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *DocumentFilter) SQLArguments() pgx.NamedArgs {
|
||||
var visibilities []string
|
||||
if f.trustCenterVisibilities != nil {
|
||||
visibilities = make([]string, len(f.trustCenterVisibilities))
|
||||
@@ -48,9 +60,11 @@ func (f *DocumentFilter) SQLArguments() pgx.StrictNamedArgs {
|
||||
visibilities[i] = v.String()
|
||||
}
|
||||
}
|
||||
return pgx.StrictNamedArgs{
|
||||
return pgx.NamedArgs{
|
||||
"query": f.query,
|
||||
"trust_center_visibilities": visibilities,
|
||||
"published": f.published,
|
||||
"user_email": f.userEmail,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,5 +85,25 @@ func (f *DocumentFilter) SQLFragment() string {
|
||||
trust_center_visibility = ANY(@trust_center_visibilities::trust_center_visibility[])
|
||||
ELSE TRUE
|
||||
END
|
||||
AND
|
||||
CASE
|
||||
WHEN @published::boolean IS NULL THEN TRUE
|
||||
WHEN @published::boolean IS TRUE THEN current_published_version IS NOT NULL
|
||||
WHEN @published::boolean IS FALSE THEN current_published_version IS NULL
|
||||
END
|
||||
AND
|
||||
CASE
|
||||
WHEN @user_email::text IS NULL THEN TRUE
|
||||
ELSE EXISTS (
|
||||
SELECT 1
|
||||
FROM document_versions dv
|
||||
INNER JOIN document_version_signatures dvs ON dv.id = dvs.document_version_id
|
||||
INNER JOIN peoples p ON dvs.signed_by = p.id
|
||||
WHERE dv.document_id = documents.id
|
||||
AND dv.status = 'PUBLISHED'
|
||||
AND p.primary_email_address = @user_email::text
|
||||
AND dvs.state IN ('REQUESTED', 'SIGNED')
|
||||
)
|
||||
END
|
||||
)`
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ func (p *DocumentVersions) LoadByDocumentID(
|
||||
scope Scoper,
|
||||
documentID gid.GID,
|
||||
cursor *page.Cursor[DocumentVersionOrderField],
|
||||
filter *DocumentVersionFilter,
|
||||
) error {
|
||||
q := `
|
||||
SELECT
|
||||
@@ -100,14 +101,16 @@ WHERE
|
||||
%s
|
||||
AND document_id = @document_id
|
||||
AND %s
|
||||
AND %s
|
||||
`
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment(), cursor.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"document_id": documentID,
|
||||
}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
maps.Copy(args, cursor.SQLArguments())
|
||||
maps.Copy(args, filter.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
|
||||
55
pkg/coredata/document_version_filter.go
Normal file
55
pkg/coredata/document_version_filter.go
Normal file
@@ -0,0 +1,55 @@
|
||||
// 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 coredata
|
||||
|
||||
import (
|
||||
"github.com/jackc/pgx/v5"
|
||||
)
|
||||
|
||||
type (
|
||||
DocumentVersionFilter struct {
|
||||
userEmail *string
|
||||
}
|
||||
)
|
||||
|
||||
func NewDocumentVersionFilter() *DocumentVersionFilter {
|
||||
return &DocumentVersionFilter{}
|
||||
}
|
||||
|
||||
func (f *DocumentVersionFilter) WithUserEmail(userEmail *string) *DocumentVersionFilter {
|
||||
f.userEmail = userEmail
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *DocumentVersionFilter) SQLArguments() pgx.StrictNamedArgs {
|
||||
return pgx.StrictNamedArgs{
|
||||
"user_email": f.userEmail,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *DocumentVersionFilter) SQLFragment() string {
|
||||
return `
|
||||
(
|
||||
@user_email::text IS NULL
|
||||
OR EXISTS (
|
||||
SELECT 1
|
||||
FROM document_version_signatures dvs
|
||||
INNER JOIN peoples p ON dvs.signed_by = p.id
|
||||
WHERE dvs.document_version_id = document_versions.id
|
||||
AND p.primary_email_address = @user_email::text
|
||||
AND dvs.state IN ('REQUESTED', 'SIGNED')
|
||||
)
|
||||
)`
|
||||
}
|
||||
@@ -20,7 +20,6 @@ type (
|
||||
|
||||
const (
|
||||
DocumentVersionOrderFieldCreatedAt DocumentVersionOrderField = "CREATED_AT"
|
||||
DocumentVersionOrderFieldVersion DocumentVersionOrderField = "VERSION"
|
||||
)
|
||||
|
||||
func (p DocumentVersionOrderField) Column() string {
|
||||
|
||||
@@ -57,6 +57,8 @@ type (
|
||||
ErrDocumentVersionSignatureAlreadyExists struct {
|
||||
message string
|
||||
}
|
||||
|
||||
ErrDocumentVersionSignatureAlreadySigned struct{}
|
||||
)
|
||||
|
||||
func (e ErrDocumentVersionSignatureNotFound) Error() string {
|
||||
@@ -67,6 +69,10 @@ func (e ErrDocumentVersionSignatureAlreadyExists) Error() string {
|
||||
return e.message
|
||||
}
|
||||
|
||||
func (e ErrDocumentVersionSignatureAlreadySigned) Error() string {
|
||||
return "document version already signed"
|
||||
}
|
||||
|
||||
func (pvs DocumentVersionSignature) CursorKey(orderBy DocumentVersionSignatureOrderField) page.CursorKey {
|
||||
switch orderBy {
|
||||
case DocumentVersionSignatureOrderFieldCreatedAt:
|
||||
@@ -412,3 +418,41 @@ WHERE
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pvs *DocumentVersionSignature) IsSignedByUserEmail(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
documentVersionID gid.GID,
|
||||
userEmail string,
|
||||
) (bool, error) {
|
||||
q := `
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM document_version_signatures dvs
|
||||
INNER JOIN peoples p ON dvs.signed_by = p.id
|
||||
WHERE dvs.document_version_id = @document_version_id
|
||||
AND p.primary_email_address = @user_email
|
||||
AND dvs.state = 'SIGNED'
|
||||
AND dvs.tenant_id = @tenant_id
|
||||
) AS signed
|
||||
`
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"document_version_id": documentVersionID,
|
||||
"user_email": userEmail,
|
||||
}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("cannot query document version signature: %w", err)
|
||||
}
|
||||
|
||||
signed, err := pgx.CollectOneRow(rows, pgx.RowTo[bool])
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("cannot collect signed status: %w", err)
|
||||
}
|
||||
|
||||
return signed, nil
|
||||
}
|
||||
|
||||
@@ -22,9 +22,10 @@ import (
|
||||
type MembershipRole string
|
||||
|
||||
const (
|
||||
MembershipRoleOwner MembershipRole = "OWNER"
|
||||
MembershipRoleAdmin MembershipRole = "ADMIN"
|
||||
MembershipRoleViewer MembershipRole = "VIEWER"
|
||||
MembershipRoleOwner MembershipRole = "OWNER"
|
||||
MembershipRoleAdmin MembershipRole = "ADMIN"
|
||||
MembershipRoleEmployee MembershipRole = "EMPLOYEE"
|
||||
MembershipRoleViewer MembershipRole = "VIEWER"
|
||||
)
|
||||
|
||||
func (r MembershipRole) String() string {
|
||||
@@ -47,6 +48,8 @@ func (r *MembershipRole) Scan(value any) error {
|
||||
*r = MembershipRoleOwner
|
||||
case "ADMIN":
|
||||
*r = MembershipRoleAdmin
|
||||
case "EMPLOYEE":
|
||||
*r = MembershipRoleEmployee
|
||||
case "VIEWER":
|
||||
*r = MembershipRoleViewer
|
||||
default:
|
||||
|
||||
1
pkg/coredata/migrations/20251113T000000Z.sql
Normal file
1
pkg/coredata/migrations/20251113T000000Z.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TYPE authz_role RENAME VALUE 'MEMBER' TO 'EMPLOYEE';
|
||||
@@ -132,24 +132,24 @@ func (p *People) LoadByEmail(
|
||||
primaryEmailAddress string,
|
||||
) error {
|
||||
q := `
|
||||
SELECT
|
||||
id,
|
||||
organization_id,
|
||||
kind,
|
||||
full_name,
|
||||
primary_email_address,
|
||||
additional_email_addresses,
|
||||
position,
|
||||
contract_start_date,
|
||||
contract_end_date,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
peoples
|
||||
WHERE
|
||||
%s
|
||||
AND primary_email_address = @primary_email_address
|
||||
LIMIT 1;
|
||||
SELECT
|
||||
id,
|
||||
organization_id,
|
||||
kind,
|
||||
full_name,
|
||||
primary_email_address,
|
||||
additional_email_addresses,
|
||||
position,
|
||||
contract_start_date,
|
||||
contract_end_date,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
peoples
|
||||
WHERE
|
||||
%s
|
||||
AND primary_email_address = @primary_email_address
|
||||
LIMIT 1;
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
@@ -176,6 +176,62 @@ func (p *People) LoadByEmail(
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *People) LoadByEmailAndOrganizationID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
primaryEmailAddress string,
|
||||
organizationID gid.GID,
|
||||
) error {
|
||||
q := `
|
||||
SELECT
|
||||
id,
|
||||
organization_id,
|
||||
kind,
|
||||
full_name,
|
||||
primary_email_address,
|
||||
additional_email_addresses,
|
||||
position,
|
||||
contract_start_date,
|
||||
contract_end_date,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
peoples
|
||||
WHERE
|
||||
%s
|
||||
AND primary_email_address = @primary_email_address
|
||||
AND organization_id = @organization_id
|
||||
LIMIT 1;
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"primary_email_address": primaryEmailAddress,
|
||||
"organization_id": organizationID,
|
||||
}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query people: %w", err)
|
||||
}
|
||||
|
||||
people, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[People])
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return &ErrPeopleNotFound{Identifier: primaryEmailAddress}
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot collect people: %w", err)
|
||||
}
|
||||
|
||||
*p = people
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Peoples) LoadByIDs(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
Reference in New Issue
Block a user