Initial work marking communications

And a bunch of lint fixes
This commit is contained in:
Eli Ribble 2026-05-04 19:07:29 +00:00
parent 67c99436d1
commit 3153e8bf13
No known key found for this signature in database
36 changed files with 1958 additions and 487 deletions

View file

@ -230,7 +230,12 @@ func webhookFieldseeker(w http.ResponseWriter, r *http.Request) {
// Write timestamp
timestamp := time.Now().Format("2006-01-02 15:04:05")
fmt.Fprintf(file, "\n=== Request logged at %s ===\n", timestamp)
_, err = fmt.Fprintf(file, "\n=== Request logged at %s ===\n", timestamp)
if err != nil {
log.Error().Err(err).Msg("writing response")
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
// Write request line
fmt.Fprintf(file, "%s %s %s\n", r.Method, r.RequestURI, r.Proto)
@ -250,7 +255,13 @@ func webhookFieldseeker(w http.ResponseWriter, r *http.Request) {
log.Printf("Error reading request body: %v", err)
fmt.Fprintf(file, "Error reading body: %v\n", err)
} else {
file.Write(body)
_, err = file.Write(body)
if err != nil {
log.Error().Err(err).Msg("writing response")
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
if len(body) == 0 {
fmt.Fprintf(file, "(empty body)")
}

View file

@ -69,6 +69,7 @@ func apiAudioContentPost(w http.ResponseWriter, r *http.Request, user platform.U
if err != nil {
log.Printf("Failed to write content file: %v", err)
http.Error(w, "failed to write content file", http.StatusInternalServerError)
return
}
ctx := r.Context()
a, err := models.NoteAudios.Query(
@ -78,8 +79,15 @@ func apiAudioContentPost(w http.ResponseWriter, r *http.Request, user platform.U
if err != nil {
log.Printf("Failed to get note audio %s for org %d: %w", u_str, user.Organization.ID, err)
http.Error(w, "failed to update database", http.StatusBadRequest)
return
}
err = background.NewAudioTranscode(ctx, db.PGInstance.BobDB, a.ID)
if err != nil {
log.Printf("Failed to transcode audio %s for org %d: %w", u_str, user.Organization.ID, err)
http.Error(w, "failed to transcode audio", http.StatusBadRequest)
return
}
background.NewAudioTranscode(ctx, db.PGInstance.BobDB, a.ID)
w.WriteHeader(http.StatusOK)
}

View file

@ -5,6 +5,7 @@ import (
"net/http"
"os"
"github.com/Gleipnir-Technology/nidus-sync/lint"
"github.com/rs/zerolog/log"
)
@ -14,7 +15,7 @@ func debugSaveRequest(r *http.Request) {
log.Error().Err(err).Msg("failed to create temp file for debugSaveRequest")
return
}
defer tmpFile.Close()
defer lint.LogOnErr(tmpFile.Close, "close temp file")
_, err = io.Copy(tmpFile, r.Body)
if err != nil {

View file

@ -30,7 +30,7 @@ type handlerFunctionGet[T any] func(context.Context, *http.Request, resource.Que
type handlerFunctionGetAuthenticated[T any] func(context.Context, *http.Request, platform.User, resource.QueryParams) (*T, *nhttp.ErrorWithStatus)
type handlerFunctionGetImage func(context.Context, *http.Request, platform.User) (file.Collection, uuid.UUID, *nhttp.ErrorWithStatus)
type handlerFunctionGetSlice[T any] func(context.Context, *http.Request, resource.QueryParams) ([]*T, *nhttp.ErrorWithStatus)
type handlerFunctionGetSliceAuthenticated[T any] func(context.Context, *http.Request, platform.User, resource.QueryParams) ([]*T, *nhttp.ErrorWithStatus)
type handlerFunctionGetSliceAuthenticated[T any] func(context.Context, *http.Request, platform.User, resource.QueryParams) ([]T, *nhttp.ErrorWithStatus)
type handlerFunctionPost[RequestType any, ResponseType any] func(context.Context, *http.Request, RequestType) (ResponseType, *nhttp.ErrorWithStatus)
type handlerFunctionPostAuthenticated[RequestType any, ResponseType any] func(context.Context, *http.Request, platform.User, RequestType) (ResponseType, *nhttp.ErrorWithStatus)
type handlerFunctionPostFormMultipart[RequestType any, ResponseType any] func(context.Context, *http.Request, RequestType) (*ResponseType, *nhttp.ErrorWithStatus)

View file

@ -99,6 +99,11 @@ func AddRoutesSync(r *mux.Router) {
r.Handle("/client/ios", auth.NewEnsureAuth(handleClientIos)).Methods("GET")
communication := resource.Communication(router)
r.Handle("/communication", authenticatedHandlerJSONSlice(communication.List)).Methods("GET")
r.Handle("/communication/{id}", authenticatedHandlerJSON(communication.Get)).Methods("GET").Name("communication.ByIDGet")
r.Handle("/communication/{id}/mark/invalid", authenticatedHandlerJSONPost(communication.MarkInvalid)).Methods("GET").Name("communication.MarkInvalid")
r.Handle("/communication/{id}/mark/pending-response", authenticatedHandlerJSONPost(communication.MarkPendingResponse)).Methods("GET").Name("communication.MarkPendingResponse")
r.Handle("/communication/{id}/mark/possible-issue", authenticatedHandlerJSONPost(communication.MarkPossibleIssue)).Methods("GET").Name("communication.MarkPossibleIssue")
r.Handle("/communication/{id}/mark/possible-resolved", authenticatedHandlerJSONPost(communication.MarkPossibleResolved)).Methods("GET").Name("communication.MarkPossibleResolved")
r.Handle("/compliance-request/mailer", authenticatedHandlerJSONPost(compliance_request.CreateMailer)).Methods("POST")
//r.HandleFunc("/compliance-request/image/pool/{public_id}", getComplianceRequestImagePool).Methods("GET")
r.Handle("/configuration/integration/arcgis", authenticatedHandlerJSONPost(postConfigurationIntegrationArcgis)).Methods("POST")

View file

@ -143,7 +143,10 @@ func (ea *EnsureAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
w.WriteHeader(401)
w.Write(msg)
_, err = w.Write(msg)
if err != nil {
log.Error().Err(err).Msg("failed to write response")
}
return
}
ea.handler(w, r, *user)

View file

@ -5,12 +5,13 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/url"
"strconv"
"github.com/Gleipnir-Technology/nidus-sync/config"
"github.com/Gleipnir-Technology/nidus-sync/lint"
"github.com/rs/zerolog/log"
)
@ -87,10 +88,10 @@ func makeVoipMSRequest(params url.Values) (VoipMSResponse, error) {
log.Warn().Err(err).Str("url", full_url).Msg("Failed to make request to Voip.MS")
return result, fmt.Errorf("Error making request: %w", err)
}
defer resp.Body.Close()
defer lint.LogOnErr(resp.Body.Close, "failed closing response body")
// Read the response body
body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Warn().Err(err).Str("url", full_url).Msg("Failed to read Voip.MS response body")
return result, fmt.Errorf("Failed to read response: %w", err)

View file

@ -87,6 +87,15 @@ var Communications = Table[
Generated: false,
AutoIncr: false,
},
OrganizationID: column{
Name: "organization_id",
DBType: "integer",
Default: "",
Comment: "",
Nullable: false,
Generated: false,
AutoIncr: false,
},
ResponseEmailLogID: column{
Name: "response_email_log_id",
DBType: "integer",
@ -150,6 +159,42 @@ var Communications = Table[
Generated: false,
AutoIncr: false,
},
SetPossibleIssue: column{
Name: "set_possible_issue",
DBType: "timestamp without time zone",
Default: "NULL",
Comment: "",
Nullable: true,
Generated: false,
AutoIncr: false,
},
SetPossibleIssueBy: column{
Name: "set_possible_issue_by",
DBType: "integer",
Default: "NULL",
Comment: "",
Nullable: true,
Generated: false,
AutoIncr: false,
},
SetPossibleResolved: column{
Name: "set_possible_resolved",
DBType: "timestamp without time zone",
Default: "NULL",
Comment: "",
Nullable: true,
Generated: false,
AutoIncr: false,
},
SetPossibleResolvedBy: column{
Name: "set_possible_resolved_by",
DBType: "integer",
Default: "NULL",
Comment: "",
Nullable: true,
Generated: false,
AutoIncr: false,
},
},
Indexes: communicationIndexes{
CommunicationPkey: index{
@ -203,6 +248,15 @@ var Communications = Table[
ForeignTable: "user_",
ForeignColumns: []string{"id"},
},
CommunicationCommunicationOrganizationIDFkey: foreignKey{
constraint: constraint{
Name: "communication.communication_organization_id_fkey",
Columns: []string{"organization_id"},
Comment: "",
},
ForeignTable: "organization",
ForeignColumns: []string{"id"},
},
CommunicationCommunicationResponseEmailLogIDFkey: foreignKey{
constraint: constraint{
Name: "communication.communication_response_email_log_id_fkey",
@ -230,6 +284,24 @@ var Communications = Table[
ForeignTable: "user_",
ForeignColumns: []string{"id"},
},
CommunicationCommunicationSetPossibleIssueByFkey: foreignKey{
constraint: constraint{
Name: "communication.communication_set_possible_issue_by_fkey",
Columns: []string{"set_possible_issue_by"},
Comment: "",
},
ForeignTable: "user_",
ForeignColumns: []string{"id"},
},
CommunicationCommunicationSetPossibleResolvedByFkey: foreignKey{
constraint: constraint{
Name: "communication.communication_set_possible_resolved_by_fkey",
Columns: []string{"set_possible_resolved_by"},
Comment: "",
},
ForeignTable: "user_",
ForeignColumns: []string{"id"},
},
CommunicationCommunicationSourceEmailLogIDFkey: foreignKey{
constraint: constraint{
Name: "communication.communication_source_email_log_id_fkey",
@ -271,6 +343,7 @@ type communicationColumns struct {
InvalidatedBy column
Opened column
OpenedBy column
OrganizationID column
ResponseEmailLogID column
ResponseTextLogID column
SetPending column
@ -278,11 +351,15 @@ type communicationColumns struct {
SourceEmailLogID column
SourceReportID column
SourceTextLogID column
SetPossibleIssue column
SetPossibleIssueBy column
SetPossibleResolved column
SetPossibleResolvedBy column
}
func (c communicationColumns) AsSlice() []column {
return []column{
c.Closed, c.ClosedBy, c.Created, c.ID, c.Invalidated, c.InvalidatedBy, c.Opened, c.OpenedBy, c.ResponseEmailLogID, c.ResponseTextLogID, c.SetPending, c.SetPendingBy, c.SourceEmailLogID, c.SourceReportID, c.SourceTextLogID,
c.Closed, c.ClosedBy, c.Created, c.ID, c.Invalidated, c.InvalidatedBy, c.Opened, c.OpenedBy, c.OrganizationID, c.ResponseEmailLogID, c.ResponseTextLogID, c.SetPending, c.SetPendingBy, c.SourceEmailLogID, c.SourceReportID, c.SourceTextLogID, c.SetPossibleIssue, c.SetPossibleIssueBy, c.SetPossibleResolved, c.SetPossibleResolvedBy,
}
}
@ -300,9 +377,12 @@ type communicationForeignKeys struct {
CommunicationCommunicationClosedByFkey foreignKey
CommunicationCommunicationInvalidatedByFkey foreignKey
CommunicationCommunicationOpenedByFkey foreignKey
CommunicationCommunicationOrganizationIDFkey foreignKey
CommunicationCommunicationResponseEmailLogIDFkey foreignKey
CommunicationCommunicationResponseTextLogIDFkey foreignKey
CommunicationCommunicationSetPendingByFkey foreignKey
CommunicationCommunicationSetPossibleIssueByFkey foreignKey
CommunicationCommunicationSetPossibleResolvedByFkey foreignKey
CommunicationCommunicationSourceEmailLogIDFkey foreignKey
CommunicationCommunicationSourceReportIDFkey foreignKey
CommunicationCommunicationSourceTextLogIDFkey foreignKey
@ -310,7 +390,7 @@ type communicationForeignKeys struct {
func (f communicationForeignKeys) AsSlice() []foreignKey {
return []foreignKey{
f.CommunicationCommunicationClosedByFkey, f.CommunicationCommunicationInvalidatedByFkey, f.CommunicationCommunicationOpenedByFkey, f.CommunicationCommunicationResponseEmailLogIDFkey, f.CommunicationCommunicationResponseTextLogIDFkey, f.CommunicationCommunicationSetPendingByFkey, f.CommunicationCommunicationSourceEmailLogIDFkey, f.CommunicationCommunicationSourceReportIDFkey, f.CommunicationCommunicationSourceTextLogIDFkey,
f.CommunicationCommunicationClosedByFkey, f.CommunicationCommunicationInvalidatedByFkey, f.CommunicationCommunicationOpenedByFkey, f.CommunicationCommunicationOrganizationIDFkey, f.CommunicationCommunicationResponseEmailLogIDFkey, f.CommunicationCommunicationResponseTextLogIDFkey, f.CommunicationCommunicationSetPendingByFkey, f.CommunicationCommunicationSetPossibleIssueByFkey, f.CommunicationCommunicationSetPossibleResolvedByFkey, f.CommunicationCommunicationSourceEmailLogIDFkey, f.CommunicationCommunicationSourceReportIDFkey, f.CommunicationCommunicationSourceTextLogIDFkey,
}
}

View file

@ -62,7 +62,7 @@ var PublicreportCompliances = Table[
},
PermissionType: column{
Name: "permission_type",
DBType: "public.permissionaccesstype",
DBType: "publicreport.permissionaccesstype",
Default: "",
Comment: "",
Nullable: false,

View file

@ -1283,85 +1283,6 @@ func (e *Notificationtype) Scan(value any) error {
return nil
}
// Enum values for Permissionaccesstype
const (
PermissionaccesstypeDenied Permissionaccesstype = "denied"
PermissionaccesstypeGranted Permissionaccesstype = "granted"
PermissionaccesstypeUnselected Permissionaccesstype = "unselected"
PermissionaccesstypeWithOwner Permissionaccesstype = "with-owner"
)
func AllPermissionaccesstype() []Permissionaccesstype {
return []Permissionaccesstype{
PermissionaccesstypeDenied,
PermissionaccesstypeGranted,
PermissionaccesstypeUnselected,
PermissionaccesstypeWithOwner,
}
}
type Permissionaccesstype string
func (e Permissionaccesstype) String() string {
return string(e)
}
func (e Permissionaccesstype) Valid() bool {
switch e {
case PermissionaccesstypeDenied,
PermissionaccesstypeGranted,
PermissionaccesstypeUnselected,
PermissionaccesstypeWithOwner:
return true
default:
return false
}
}
// useful when testing in other packages
func (e Permissionaccesstype) All() []Permissionaccesstype {
return AllPermissionaccesstype()
}
func (e Permissionaccesstype) MarshalText() ([]byte, error) {
return []byte(e), nil
}
func (e *Permissionaccesstype) UnmarshalText(text []byte) error {
return e.Scan(text)
}
func (e Permissionaccesstype) MarshalBinary() ([]byte, error) {
return []byte(e), nil
}
func (e *Permissionaccesstype) UnmarshalBinary(data []byte) error {
return e.Scan(data)
}
func (e Permissionaccesstype) Value() (driver.Value, error) {
return string(e), nil
}
func (e *Permissionaccesstype) Scan(value any) error {
switch x := value.(type) {
case string:
*e = Permissionaccesstype(x)
case []byte:
*e = Permissionaccesstype(x)
case nil:
return fmt.Errorf("cannot nil into Permissionaccesstype")
default:
return fmt.Errorf("cannot scan type %T: %v", value, value)
}
if !e.Valid() {
return fmt.Errorf("invalid Permissionaccesstype value: %s", *e)
}
return nil
}
// Enum values for Poolconditiontype
const (
PoolconditiontypeBlue Poolconditiontype = "blue"
@ -1629,6 +1550,85 @@ func (e *PublicreportNuisancedurationtype) Scan(value any) error {
return nil
}
// Enum values for PublicreportPermissionaccesstype
const (
PublicreportPermissionaccesstypeDenied PublicreportPermissionaccesstype = "denied"
PublicreportPermissionaccesstypeGranted PublicreportPermissionaccesstype = "granted"
PublicreportPermissionaccesstypeUnselected PublicreportPermissionaccesstype = "unselected"
PublicreportPermissionaccesstypeWithOwner PublicreportPermissionaccesstype = "with-owner"
)
func AllPublicreportPermissionaccesstype() []PublicreportPermissionaccesstype {
return []PublicreportPermissionaccesstype{
PublicreportPermissionaccesstypeDenied,
PublicreportPermissionaccesstypeGranted,
PublicreportPermissionaccesstypeUnselected,
PublicreportPermissionaccesstypeWithOwner,
}
}
type PublicreportPermissionaccesstype string
func (e PublicreportPermissionaccesstype) String() string {
return string(e)
}
func (e PublicreportPermissionaccesstype) Valid() bool {
switch e {
case PublicreportPermissionaccesstypeDenied,
PublicreportPermissionaccesstypeGranted,
PublicreportPermissionaccesstypeUnselected,
PublicreportPermissionaccesstypeWithOwner:
return true
default:
return false
}
}
// useful when testing in other packages
func (e PublicreportPermissionaccesstype) All() []PublicreportPermissionaccesstype {
return AllPublicreportPermissionaccesstype()
}
func (e PublicreportPermissionaccesstype) MarshalText() ([]byte, error) {
return []byte(e), nil
}
func (e *PublicreportPermissionaccesstype) UnmarshalText(text []byte) error {
return e.Scan(text)
}
func (e PublicreportPermissionaccesstype) MarshalBinary() ([]byte, error) {
return []byte(e), nil
}
func (e *PublicreportPermissionaccesstype) UnmarshalBinary(data []byte) error {
return e.Scan(data)
}
func (e PublicreportPermissionaccesstype) Value() (driver.Value, error) {
return string(e), nil
}
func (e *PublicreportPermissionaccesstype) Scan(value any) error {
switch x := value.(type) {
case string:
*e = PublicreportPermissionaccesstype(x)
case []byte:
*e = PublicreportPermissionaccesstype(x)
case nil:
return fmt.Errorf("cannot nil into PublicreportPermissionaccesstype")
default:
return fmt.Errorf("cannot scan type %T: %v", value, value)
}
if !e.Valid() {
return fmt.Errorf("invalid PublicreportPermissionaccesstype value: %s", *e)
}
return nil
}
// Enum values for PublicreportPoolsourceduration
const (
PublicreportPoolsourcedurationNone PublicreportPoolsourceduration = "none"

View file

@ -0,0 +1,6 @@
-- +goose Up
ALTER TABLE communication
ADD COLUMN set_possible_issue TIMESTAMP WITHOUT TIME ZONE,
ADD COLUMN set_possible_issue_by INTEGER REFERENCES user_(id),
ADD COLUMN set_possible_resolved TIMESTAMP WITHOUT TIME ZONE,
ADD COLUMN set_possible_resolved_by INTEGER REFERENCES user_(id);

View file

@ -33,6 +33,7 @@ type Communication struct {
InvalidatedBy null.Val[int32] `db:"invalidated_by" `
Opened null.Val[time.Time] `db:"opened" `
OpenedBy null.Val[int32] `db:"opened_by" `
OrganizationID int32 `db:"organization_id" `
ResponseEmailLogID null.Val[int32] `db:"response_email_log_id" `
ResponseTextLogID null.Val[int32] `db:"response_text_log_id" `
SetPending null.Val[time.Time] `db:"set_pending" `
@ -40,6 +41,10 @@ type Communication struct {
SourceEmailLogID null.Val[int32] `db:"source_email_log_id" `
SourceReportID null.Val[int32] `db:"source_report_id" `
SourceTextLogID null.Val[int32] `db:"source_text_log_id" `
SetPossibleIssue null.Val[time.Time] `db:"set_possible_issue" `
SetPossibleIssueBy null.Val[int32] `db:"set_possible_issue_by" `
SetPossibleResolved null.Val[time.Time] `db:"set_possible_resolved" `
SetPossibleResolvedBy null.Val[int32] `db:"set_possible_resolved_by" `
R communicationR `db:"-" `
}
@ -59,9 +64,12 @@ type communicationR struct {
ClosedByUser *User // communication.communication_closed_by_fkey
InvalidatedByUser *User // communication.communication_invalidated_by_fkey
OpenedByUser *User // communication.communication_opened_by_fkey
Organization *Organization // communication.communication_organization_id_fkey
ResponseEmailLogEmailLog *CommsEmailLog // communication.communication_response_email_log_id_fkey
ResponseTextLogTextLog *CommsTextLog // communication.communication_response_text_log_id_fkey
SetPendingByUser *User // communication.communication_set_pending_by_fkey
SetPossibleIssueByUser *User // communication.communication_set_possible_issue_by_fkey
SetPossibleResolvedByUser *User // communication.communication_set_possible_resolved_by_fkey
SourceEmailLogEmailLog *CommsEmailLog // communication.communication_source_email_log_id_fkey
SourceReportReport *PublicreportReport // communication.communication_source_report_id_fkey
SourceTextLogTextLog *CommsTextLog // communication.communication_source_text_log_id_fkey
@ -70,7 +78,7 @@ type communicationR struct {
func buildCommunicationColumns(alias string) communicationColumns {
return communicationColumns{
ColumnsExpr: expr.NewColumnsExpr(
"closed", "closed_by", "created", "id", "invalidated", "invalidated_by", "opened", "opened_by", "response_email_log_id", "response_text_log_id", "set_pending", "set_pending_by", "source_email_log_id", "source_report_id", "source_text_log_id",
"closed", "closed_by", "created", "id", "invalidated", "invalidated_by", "opened", "opened_by", "organization_id", "response_email_log_id", "response_text_log_id", "set_pending", "set_pending_by", "source_email_log_id", "source_report_id", "source_text_log_id", "set_possible_issue", "set_possible_issue_by", "set_possible_resolved", "set_possible_resolved_by",
).WithParent("communication"),
tableAlias: alias,
Closed: psql.Quote(alias, "closed"),
@ -81,6 +89,7 @@ func buildCommunicationColumns(alias string) communicationColumns {
InvalidatedBy: psql.Quote(alias, "invalidated_by"),
Opened: psql.Quote(alias, "opened"),
OpenedBy: psql.Quote(alias, "opened_by"),
OrganizationID: psql.Quote(alias, "organization_id"),
ResponseEmailLogID: psql.Quote(alias, "response_email_log_id"),
ResponseTextLogID: psql.Quote(alias, "response_text_log_id"),
SetPending: psql.Quote(alias, "set_pending"),
@ -88,6 +97,10 @@ func buildCommunicationColumns(alias string) communicationColumns {
SourceEmailLogID: psql.Quote(alias, "source_email_log_id"),
SourceReportID: psql.Quote(alias, "source_report_id"),
SourceTextLogID: psql.Quote(alias, "source_text_log_id"),
SetPossibleIssue: psql.Quote(alias, "set_possible_issue"),
SetPossibleIssueBy: psql.Quote(alias, "set_possible_issue_by"),
SetPossibleResolved: psql.Quote(alias, "set_possible_resolved"),
SetPossibleResolvedBy: psql.Quote(alias, "set_possible_resolved_by"),
}
}
@ -102,6 +115,7 @@ type communicationColumns struct {
InvalidatedBy psql.Expression
Opened psql.Expression
OpenedBy psql.Expression
OrganizationID psql.Expression
ResponseEmailLogID psql.Expression
ResponseTextLogID psql.Expression
SetPending psql.Expression
@ -109,6 +123,10 @@ type communicationColumns struct {
SourceEmailLogID psql.Expression
SourceReportID psql.Expression
SourceTextLogID psql.Expression
SetPossibleIssue psql.Expression
SetPossibleIssueBy psql.Expression
SetPossibleResolved psql.Expression
SetPossibleResolvedBy psql.Expression
}
func (c communicationColumns) Alias() string {
@ -131,6 +149,7 @@ type CommunicationSetter struct {
InvalidatedBy omitnull.Val[int32] `db:"invalidated_by" `
Opened omitnull.Val[time.Time] `db:"opened" `
OpenedBy omitnull.Val[int32] `db:"opened_by" `
OrganizationID omit.Val[int32] `db:"organization_id" `
ResponseEmailLogID omitnull.Val[int32] `db:"response_email_log_id" `
ResponseTextLogID omitnull.Val[int32] `db:"response_text_log_id" `
SetPending omitnull.Val[time.Time] `db:"set_pending" `
@ -138,10 +157,14 @@ type CommunicationSetter struct {
SourceEmailLogID omitnull.Val[int32] `db:"source_email_log_id" `
SourceReportID omitnull.Val[int32] `db:"source_report_id" `
SourceTextLogID omitnull.Val[int32] `db:"source_text_log_id" `
SetPossibleIssue omitnull.Val[time.Time] `db:"set_possible_issue" `
SetPossibleIssueBy omitnull.Val[int32] `db:"set_possible_issue_by" `
SetPossibleResolved omitnull.Val[time.Time] `db:"set_possible_resolved" `
SetPossibleResolvedBy omitnull.Val[int32] `db:"set_possible_resolved_by" `
}
func (s CommunicationSetter) SetColumns() []string {
vals := make([]string, 0, 15)
vals := make([]string, 0, 20)
if !s.Closed.IsUnset() {
vals = append(vals, "closed")
}
@ -166,6 +189,9 @@ func (s CommunicationSetter) SetColumns() []string {
if !s.OpenedBy.IsUnset() {
vals = append(vals, "opened_by")
}
if s.OrganizationID.IsValue() {
vals = append(vals, "organization_id")
}
if !s.ResponseEmailLogID.IsUnset() {
vals = append(vals, "response_email_log_id")
}
@ -187,6 +213,18 @@ func (s CommunicationSetter) SetColumns() []string {
if !s.SourceTextLogID.IsUnset() {
vals = append(vals, "source_text_log_id")
}
if !s.SetPossibleIssue.IsUnset() {
vals = append(vals, "set_possible_issue")
}
if !s.SetPossibleIssueBy.IsUnset() {
vals = append(vals, "set_possible_issue_by")
}
if !s.SetPossibleResolved.IsUnset() {
vals = append(vals, "set_possible_resolved")
}
if !s.SetPossibleResolvedBy.IsUnset() {
vals = append(vals, "set_possible_resolved_by")
}
return vals
}
@ -215,6 +253,9 @@ func (s CommunicationSetter) Overwrite(t *Communication) {
if !s.OpenedBy.IsUnset() {
t.OpenedBy = s.OpenedBy.MustGetNull()
}
if s.OrganizationID.IsValue() {
t.OrganizationID = s.OrganizationID.MustGet()
}
if !s.ResponseEmailLogID.IsUnset() {
t.ResponseEmailLogID = s.ResponseEmailLogID.MustGetNull()
}
@ -236,6 +277,18 @@ func (s CommunicationSetter) Overwrite(t *Communication) {
if !s.SourceTextLogID.IsUnset() {
t.SourceTextLogID = s.SourceTextLogID.MustGetNull()
}
if !s.SetPossibleIssue.IsUnset() {
t.SetPossibleIssue = s.SetPossibleIssue.MustGetNull()
}
if !s.SetPossibleIssueBy.IsUnset() {
t.SetPossibleIssueBy = s.SetPossibleIssueBy.MustGetNull()
}
if !s.SetPossibleResolved.IsUnset() {
t.SetPossibleResolved = s.SetPossibleResolved.MustGetNull()
}
if !s.SetPossibleResolvedBy.IsUnset() {
t.SetPossibleResolvedBy = s.SetPossibleResolvedBy.MustGetNull()
}
}
func (s *CommunicationSetter) Apply(q *dialect.InsertQuery) {
@ -244,7 +297,7 @@ func (s *CommunicationSetter) Apply(q *dialect.InsertQuery) {
})
q.AppendValues(bob.ExpressionFunc(func(ctx context.Context, w io.StringWriter, d bob.Dialect, start int) ([]any, error) {
vals := make([]bob.Expression, 15)
vals := make([]bob.Expression, 20)
if !s.Closed.IsUnset() {
vals[0] = psql.Arg(s.Closed.MustGetNull())
} else {
@ -293,48 +346,78 @@ func (s *CommunicationSetter) Apply(q *dialect.InsertQuery) {
vals[7] = psql.Raw("DEFAULT")
}
if !s.ResponseEmailLogID.IsUnset() {
vals[8] = psql.Arg(s.ResponseEmailLogID.MustGetNull())
if s.OrganizationID.IsValue() {
vals[8] = psql.Arg(s.OrganizationID.MustGet())
} else {
vals[8] = psql.Raw("DEFAULT")
}
if !s.ResponseTextLogID.IsUnset() {
vals[9] = psql.Arg(s.ResponseTextLogID.MustGetNull())
if !s.ResponseEmailLogID.IsUnset() {
vals[9] = psql.Arg(s.ResponseEmailLogID.MustGetNull())
} else {
vals[9] = psql.Raw("DEFAULT")
}
if !s.SetPending.IsUnset() {
vals[10] = psql.Arg(s.SetPending.MustGetNull())
if !s.ResponseTextLogID.IsUnset() {
vals[10] = psql.Arg(s.ResponseTextLogID.MustGetNull())
} else {
vals[10] = psql.Raw("DEFAULT")
}
if !s.SetPendingBy.IsUnset() {
vals[11] = psql.Arg(s.SetPendingBy.MustGetNull())
if !s.SetPending.IsUnset() {
vals[11] = psql.Arg(s.SetPending.MustGetNull())
} else {
vals[11] = psql.Raw("DEFAULT")
}
if !s.SourceEmailLogID.IsUnset() {
vals[12] = psql.Arg(s.SourceEmailLogID.MustGetNull())
if !s.SetPendingBy.IsUnset() {
vals[12] = psql.Arg(s.SetPendingBy.MustGetNull())
} else {
vals[12] = psql.Raw("DEFAULT")
}
if !s.SourceReportID.IsUnset() {
vals[13] = psql.Arg(s.SourceReportID.MustGetNull())
if !s.SourceEmailLogID.IsUnset() {
vals[13] = psql.Arg(s.SourceEmailLogID.MustGetNull())
} else {
vals[13] = psql.Raw("DEFAULT")
}
if !s.SourceTextLogID.IsUnset() {
vals[14] = psql.Arg(s.SourceTextLogID.MustGetNull())
if !s.SourceReportID.IsUnset() {
vals[14] = psql.Arg(s.SourceReportID.MustGetNull())
} else {
vals[14] = psql.Raw("DEFAULT")
}
if !s.SourceTextLogID.IsUnset() {
vals[15] = psql.Arg(s.SourceTextLogID.MustGetNull())
} else {
vals[15] = psql.Raw("DEFAULT")
}
if !s.SetPossibleIssue.IsUnset() {
vals[16] = psql.Arg(s.SetPossibleIssue.MustGetNull())
} else {
vals[16] = psql.Raw("DEFAULT")
}
if !s.SetPossibleIssueBy.IsUnset() {
vals[17] = psql.Arg(s.SetPossibleIssueBy.MustGetNull())
} else {
vals[17] = psql.Raw("DEFAULT")
}
if !s.SetPossibleResolved.IsUnset() {
vals[18] = psql.Arg(s.SetPossibleResolved.MustGetNull())
} else {
vals[18] = psql.Raw("DEFAULT")
}
if !s.SetPossibleResolvedBy.IsUnset() {
vals[19] = psql.Arg(s.SetPossibleResolvedBy.MustGetNull())
} else {
vals[19] = psql.Raw("DEFAULT")
}
return bob.ExpressSlice(ctx, w, d, start, vals, "", ", ", "")
}))
}
@ -344,7 +427,7 @@ func (s CommunicationSetter) UpdateMod() bob.Mod[*dialect.UpdateQuery] {
}
func (s CommunicationSetter) Expressions(prefix ...string) []bob.Expression {
exprs := make([]bob.Expression, 0, 15)
exprs := make([]bob.Expression, 0, 20)
if !s.Closed.IsUnset() {
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
@ -402,6 +485,13 @@ func (s CommunicationSetter) Expressions(prefix ...string) []bob.Expression {
}})
}
if s.OrganizationID.IsValue() {
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
psql.Quote(append(prefix, "organization_id")...),
psql.Arg(s.OrganizationID),
}})
}
if !s.ResponseEmailLogID.IsUnset() {
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
psql.Quote(append(prefix, "response_email_log_id")...),
@ -451,6 +541,34 @@ func (s CommunicationSetter) Expressions(prefix ...string) []bob.Expression {
}})
}
if !s.SetPossibleIssue.IsUnset() {
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
psql.Quote(append(prefix, "set_possible_issue")...),
psql.Arg(s.SetPossibleIssue),
}})
}
if !s.SetPossibleIssueBy.IsUnset() {
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
psql.Quote(append(prefix, "set_possible_issue_by")...),
psql.Arg(s.SetPossibleIssueBy),
}})
}
if !s.SetPossibleResolved.IsUnset() {
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
psql.Quote(append(prefix, "set_possible_resolved")...),
psql.Arg(s.SetPossibleResolved),
}})
}
if !s.SetPossibleResolvedBy.IsUnset() {
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
psql.Quote(append(prefix, "set_possible_resolved_by")...),
psql.Arg(s.SetPossibleResolvedBy),
}})
}
return exprs
}
@ -749,6 +867,30 @@ func (os CommunicationSlice) OpenedByUser(mods ...bob.Mod[*dialect.SelectQuery])
)...)
}
// Organization starts a query for related objects on organization
func (o *Communication) Organization(mods ...bob.Mod[*dialect.SelectQuery]) OrganizationsQuery {
return Organizations.Query(append(mods,
sm.Where(Organizations.Columns.ID.EQ(psql.Arg(o.OrganizationID))),
)...)
}
func (os CommunicationSlice) Organization(mods ...bob.Mod[*dialect.SelectQuery]) OrganizationsQuery {
pkOrganizationID := make(pgtypes.Array[int32], 0, len(os))
for _, o := range os {
if o == nil {
continue
}
pkOrganizationID = append(pkOrganizationID, o.OrganizationID)
}
PKArgExpr := psql.Select(sm.Columns(
psql.F("unnest", psql.Cast(psql.Arg(pkOrganizationID), "integer[]")),
))
return Organizations.Query(append(mods,
sm.Where(psql.Group(Organizations.Columns.ID).OP("IN", PKArgExpr)),
)...)
}
// ResponseEmailLogEmailLog starts a query for related objects on comms.email_log
func (o *Communication) ResponseEmailLogEmailLog(mods ...bob.Mod[*dialect.SelectQuery]) CommsEmailLogsQuery {
return CommsEmailLogs.Query(append(mods,
@ -821,6 +963,54 @@ func (os CommunicationSlice) SetPendingByUser(mods ...bob.Mod[*dialect.SelectQue
)...)
}
// SetPossibleIssueByUser starts a query for related objects on user_
func (o *Communication) SetPossibleIssueByUser(mods ...bob.Mod[*dialect.SelectQuery]) UsersQuery {
return Users.Query(append(mods,
sm.Where(Users.Columns.ID.EQ(psql.Arg(o.SetPossibleIssueBy))),
)...)
}
func (os CommunicationSlice) SetPossibleIssueByUser(mods ...bob.Mod[*dialect.SelectQuery]) UsersQuery {
pkSetPossibleIssueBy := make(pgtypes.Array[null.Val[int32]], 0, len(os))
for _, o := range os {
if o == nil {
continue
}
pkSetPossibleIssueBy = append(pkSetPossibleIssueBy, o.SetPossibleIssueBy)
}
PKArgExpr := psql.Select(sm.Columns(
psql.F("unnest", psql.Cast(psql.Arg(pkSetPossibleIssueBy), "integer[]")),
))
return Users.Query(append(mods,
sm.Where(psql.Group(Users.Columns.ID).OP("IN", PKArgExpr)),
)...)
}
// SetPossibleResolvedByUser starts a query for related objects on user_
func (o *Communication) SetPossibleResolvedByUser(mods ...bob.Mod[*dialect.SelectQuery]) UsersQuery {
return Users.Query(append(mods,
sm.Where(Users.Columns.ID.EQ(psql.Arg(o.SetPossibleResolvedBy))),
)...)
}
func (os CommunicationSlice) SetPossibleResolvedByUser(mods ...bob.Mod[*dialect.SelectQuery]) UsersQuery {
pkSetPossibleResolvedBy := make(pgtypes.Array[null.Val[int32]], 0, len(os))
for _, o := range os {
if o == nil {
continue
}
pkSetPossibleResolvedBy = append(pkSetPossibleResolvedBy, o.SetPossibleResolvedBy)
}
PKArgExpr := psql.Select(sm.Columns(
psql.F("unnest", psql.Cast(psql.Arg(pkSetPossibleResolvedBy), "integer[]")),
))
return Users.Query(append(mods,
sm.Where(psql.Group(Users.Columns.ID).OP("IN", PKArgExpr)),
)...)
}
// SourceEmailLogEmailLog starts a query for related objects on comms.email_log
func (o *Communication) SourceEmailLogEmailLog(mods ...bob.Mod[*dialect.SelectQuery]) CommsEmailLogsQuery {
return CommsEmailLogs.Query(append(mods,
@ -1037,6 +1227,54 @@ func (communication0 *Communication) AttachOpenedByUser(ctx context.Context, exe
return nil
}
func attachCommunicationOrganization0(ctx context.Context, exec bob.Executor, count int, communication0 *Communication, organization1 *Organization) (*Communication, error) {
setter := &CommunicationSetter{
OrganizationID: omit.From(organization1.ID),
}
err := communication0.Update(ctx, exec, setter)
if err != nil {
return nil, fmt.Errorf("attachCommunicationOrganization0: %w", err)
}
return communication0, nil
}
func (communication0 *Communication) InsertOrganization(ctx context.Context, exec bob.Executor, related *OrganizationSetter) error {
var err error
organization1, err := Organizations.Insert(related).One(ctx, exec)
if err != nil {
return fmt.Errorf("inserting related objects: %w", err)
}
_, err = attachCommunicationOrganization0(ctx, exec, 1, communication0, organization1)
if err != nil {
return err
}
communication0.R.Organization = organization1
organization1.R.Communications = append(organization1.R.Communications, communication0)
return nil
}
func (communication0 *Communication) AttachOrganization(ctx context.Context, exec bob.Executor, organization1 *Organization) error {
var err error
_, err = attachCommunicationOrganization0(ctx, exec, 1, communication0, organization1)
if err != nil {
return err
}
communication0.R.Organization = organization1
organization1.R.Communications = append(organization1.R.Communications, communication0)
return nil
}
func attachCommunicationResponseEmailLogEmailLog0(ctx context.Context, exec bob.Executor, count int, communication0 *Communication, commsEmailLog1 *CommsEmailLog) (*Communication, error) {
setter := &CommunicationSetter{
ResponseEmailLogID: omitnull.From(commsEmailLog1.ID),
@ -1181,6 +1419,102 @@ func (communication0 *Communication) AttachSetPendingByUser(ctx context.Context,
return nil
}
func attachCommunicationSetPossibleIssueByUser0(ctx context.Context, exec bob.Executor, count int, communication0 *Communication, user1 *User) (*Communication, error) {
setter := &CommunicationSetter{
SetPossibleIssueBy: omitnull.From(user1.ID),
}
err := communication0.Update(ctx, exec, setter)
if err != nil {
return nil, fmt.Errorf("attachCommunicationSetPossibleIssueByUser0: %w", err)
}
return communication0, nil
}
func (communication0 *Communication) InsertSetPossibleIssueByUser(ctx context.Context, exec bob.Executor, related *UserSetter) error {
var err error
user1, err := Users.Insert(related).One(ctx, exec)
if err != nil {
return fmt.Errorf("inserting related objects: %w", err)
}
_, err = attachCommunicationSetPossibleIssueByUser0(ctx, exec, 1, communication0, user1)
if err != nil {
return err
}
communication0.R.SetPossibleIssueByUser = user1
user1.R.SetPossibleIssueByCommunications = append(user1.R.SetPossibleIssueByCommunications, communication0)
return nil
}
func (communication0 *Communication) AttachSetPossibleIssueByUser(ctx context.Context, exec bob.Executor, user1 *User) error {
var err error
_, err = attachCommunicationSetPossibleIssueByUser0(ctx, exec, 1, communication0, user1)
if err != nil {
return err
}
communication0.R.SetPossibleIssueByUser = user1
user1.R.SetPossibleIssueByCommunications = append(user1.R.SetPossibleIssueByCommunications, communication0)
return nil
}
func attachCommunicationSetPossibleResolvedByUser0(ctx context.Context, exec bob.Executor, count int, communication0 *Communication, user1 *User) (*Communication, error) {
setter := &CommunicationSetter{
SetPossibleResolvedBy: omitnull.From(user1.ID),
}
err := communication0.Update(ctx, exec, setter)
if err != nil {
return nil, fmt.Errorf("attachCommunicationSetPossibleResolvedByUser0: %w", err)
}
return communication0, nil
}
func (communication0 *Communication) InsertSetPossibleResolvedByUser(ctx context.Context, exec bob.Executor, related *UserSetter) error {
var err error
user1, err := Users.Insert(related).One(ctx, exec)
if err != nil {
return fmt.Errorf("inserting related objects: %w", err)
}
_, err = attachCommunicationSetPossibleResolvedByUser0(ctx, exec, 1, communication0, user1)
if err != nil {
return err
}
communication0.R.SetPossibleResolvedByUser = user1
user1.R.SetPossibleResolvedByCommunications = append(user1.R.SetPossibleResolvedByCommunications, communication0)
return nil
}
func (communication0 *Communication) AttachSetPossibleResolvedByUser(ctx context.Context, exec bob.Executor, user1 *User) error {
var err error
_, err = attachCommunicationSetPossibleResolvedByUser0(ctx, exec, 1, communication0, user1)
if err != nil {
return err
}
communication0.R.SetPossibleResolvedByUser = user1
user1.R.SetPossibleResolvedByCommunications = append(user1.R.SetPossibleResolvedByCommunications, communication0)
return nil
}
func attachCommunicationSourceEmailLogEmailLog0(ctx context.Context, exec bob.Executor, count int, communication0 *Communication, commsEmailLog1 *CommsEmailLog) (*Communication, error) {
setter := &CommunicationSetter{
SourceEmailLogID: omitnull.From(commsEmailLog1.ID),
@ -1334,6 +1668,7 @@ type communicationWhere[Q psql.Filterable] struct {
InvalidatedBy psql.WhereNullMod[Q, int32]
Opened psql.WhereNullMod[Q, time.Time]
OpenedBy psql.WhereNullMod[Q, int32]
OrganizationID psql.WhereMod[Q, int32]
ResponseEmailLogID psql.WhereNullMod[Q, int32]
ResponseTextLogID psql.WhereNullMod[Q, int32]
SetPending psql.WhereNullMod[Q, time.Time]
@ -1341,6 +1676,10 @@ type communicationWhere[Q psql.Filterable] struct {
SourceEmailLogID psql.WhereNullMod[Q, int32]
SourceReportID psql.WhereNullMod[Q, int32]
SourceTextLogID psql.WhereNullMod[Q, int32]
SetPossibleIssue psql.WhereNullMod[Q, time.Time]
SetPossibleIssueBy psql.WhereNullMod[Q, int32]
SetPossibleResolved psql.WhereNullMod[Q, time.Time]
SetPossibleResolvedBy psql.WhereNullMod[Q, int32]
}
func (communicationWhere[Q]) AliasedAs(alias string) communicationWhere[Q] {
@ -1357,6 +1696,7 @@ func buildCommunicationWhere[Q psql.Filterable](cols communicationColumns) commu
InvalidatedBy: psql.WhereNull[Q, int32](cols.InvalidatedBy),
Opened: psql.WhereNull[Q, time.Time](cols.Opened),
OpenedBy: psql.WhereNull[Q, int32](cols.OpenedBy),
OrganizationID: psql.Where[Q, int32](cols.OrganizationID),
ResponseEmailLogID: psql.WhereNull[Q, int32](cols.ResponseEmailLogID),
ResponseTextLogID: psql.WhereNull[Q, int32](cols.ResponseTextLogID),
SetPending: psql.WhereNull[Q, time.Time](cols.SetPending),
@ -1364,6 +1704,10 @@ func buildCommunicationWhere[Q psql.Filterable](cols communicationColumns) commu
SourceEmailLogID: psql.WhereNull[Q, int32](cols.SourceEmailLogID),
SourceReportID: psql.WhereNull[Q, int32](cols.SourceReportID),
SourceTextLogID: psql.WhereNull[Q, int32](cols.SourceTextLogID),
SetPossibleIssue: psql.WhereNull[Q, time.Time](cols.SetPossibleIssue),
SetPossibleIssueBy: psql.WhereNull[Q, int32](cols.SetPossibleIssueBy),
SetPossibleResolved: psql.WhereNull[Q, time.Time](cols.SetPossibleResolved),
SetPossibleResolvedBy: psql.WhereNull[Q, int32](cols.SetPossibleResolvedBy),
}
}
@ -1409,6 +1753,18 @@ func (o *Communication) Preload(name string, retrieved any) error {
rel.R.OpenedByCommunications = CommunicationSlice{o}
}
return nil
case "Organization":
rel, ok := retrieved.(*Organization)
if !ok {
return fmt.Errorf("communication cannot load %T as %q", retrieved, name)
}
o.R.Organization = rel
if rel != nil {
rel.R.Communications = CommunicationSlice{o}
}
return nil
case "ResponseEmailLogEmailLog":
rel, ok := retrieved.(*CommsEmailLog)
if !ok {
@ -1445,6 +1801,30 @@ func (o *Communication) Preload(name string, retrieved any) error {
rel.R.SetPendingByCommunications = CommunicationSlice{o}
}
return nil
case "SetPossibleIssueByUser":
rel, ok := retrieved.(*User)
if !ok {
return fmt.Errorf("communication cannot load %T as %q", retrieved, name)
}
o.R.SetPossibleIssueByUser = rel
if rel != nil {
rel.R.SetPossibleIssueByCommunications = CommunicationSlice{o}
}
return nil
case "SetPossibleResolvedByUser":
rel, ok := retrieved.(*User)
if !ok {
return fmt.Errorf("communication cannot load %T as %q", retrieved, name)
}
o.R.SetPossibleResolvedByUser = rel
if rel != nil {
rel.R.SetPossibleResolvedByCommunications = CommunicationSlice{o}
}
return nil
case "SourceEmailLogEmailLog":
rel, ok := retrieved.(*CommsEmailLog)
if !ok {
@ -1490,9 +1870,12 @@ type communicationPreloader struct {
ClosedByUser func(...psql.PreloadOption) psql.Preloader
InvalidatedByUser func(...psql.PreloadOption) psql.Preloader
OpenedByUser func(...psql.PreloadOption) psql.Preloader
Organization func(...psql.PreloadOption) psql.Preloader
ResponseEmailLogEmailLog func(...psql.PreloadOption) psql.Preloader
ResponseTextLogTextLog func(...psql.PreloadOption) psql.Preloader
SetPendingByUser func(...psql.PreloadOption) psql.Preloader
SetPossibleIssueByUser func(...psql.PreloadOption) psql.Preloader
SetPossibleResolvedByUser func(...psql.PreloadOption) psql.Preloader
SourceEmailLogEmailLog func(...psql.PreloadOption) psql.Preloader
SourceReportReport func(...psql.PreloadOption) psql.Preloader
SourceTextLogTextLog func(...psql.PreloadOption) psql.Preloader
@ -1539,6 +1922,19 @@ func buildCommunicationPreloader() communicationPreloader {
},
}, Users.Columns.Names(), opts...)
},
Organization: func(opts ...psql.PreloadOption) psql.Preloader {
return psql.Preload[*Organization, OrganizationSlice](psql.PreloadRel{
Name: "Organization",
Sides: []psql.PreloadSide{
{
From: Communications,
To: Organizations,
FromColumns: []string{"organization_id"},
ToColumns: []string{"id"},
},
},
}, Organizations.Columns.Names(), opts...)
},
ResponseEmailLogEmailLog: func(opts ...psql.PreloadOption) psql.Preloader {
return psql.Preload[*CommsEmailLog, CommsEmailLogSlice](psql.PreloadRel{
Name: "ResponseEmailLogEmailLog",
@ -1578,6 +1974,32 @@ func buildCommunicationPreloader() communicationPreloader {
},
}, Users.Columns.Names(), opts...)
},
SetPossibleIssueByUser: func(opts ...psql.PreloadOption) psql.Preloader {
return psql.Preload[*User, UserSlice](psql.PreloadRel{
Name: "SetPossibleIssueByUser",
Sides: []psql.PreloadSide{
{
From: Communications,
To: Users,
FromColumns: []string{"set_possible_issue_by"},
ToColumns: []string{"id"},
},
},
}, Users.Columns.Names(), opts...)
},
SetPossibleResolvedByUser: func(opts ...psql.PreloadOption) psql.Preloader {
return psql.Preload[*User, UserSlice](psql.PreloadRel{
Name: "SetPossibleResolvedByUser",
Sides: []psql.PreloadSide{
{
From: Communications,
To: Users,
FromColumns: []string{"set_possible_resolved_by"},
ToColumns: []string{"id"},
},
},
}, Users.Columns.Names(), opts...)
},
SourceEmailLogEmailLog: func(opts ...psql.PreloadOption) psql.Preloader {
return psql.Preload[*CommsEmailLog, CommsEmailLogSlice](psql.PreloadRel{
Name: "SourceEmailLogEmailLog",
@ -1624,9 +2046,12 @@ type communicationThenLoader[Q orm.Loadable] struct {
ClosedByUser func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
InvalidatedByUser func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
OpenedByUser func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
Organization func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
ResponseEmailLogEmailLog func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
ResponseTextLogTextLog func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
SetPendingByUser func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
SetPossibleIssueByUser func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
SetPossibleResolvedByUser func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
SourceEmailLogEmailLog func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
SourceReportReport func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
SourceTextLogTextLog func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
@ -1642,6 +2067,9 @@ func buildCommunicationThenLoader[Q orm.Loadable]() communicationThenLoader[Q] {
type OpenedByUserLoadInterface interface {
LoadOpenedByUser(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
}
type OrganizationLoadInterface interface {
LoadOrganization(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
}
type ResponseEmailLogEmailLogLoadInterface interface {
LoadResponseEmailLogEmailLog(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
}
@ -1651,6 +2079,12 @@ func buildCommunicationThenLoader[Q orm.Loadable]() communicationThenLoader[Q] {
type SetPendingByUserLoadInterface interface {
LoadSetPendingByUser(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
}
type SetPossibleIssueByUserLoadInterface interface {
LoadSetPossibleIssueByUser(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
}
type SetPossibleResolvedByUserLoadInterface interface {
LoadSetPossibleResolvedByUser(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
}
type SourceEmailLogEmailLogLoadInterface interface {
LoadSourceEmailLogEmailLog(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
}
@ -1680,6 +2114,12 @@ func buildCommunicationThenLoader[Q orm.Loadable]() communicationThenLoader[Q] {
return retrieved.LoadOpenedByUser(ctx, exec, mods...)
},
),
Organization: thenLoadBuilder[Q](
"Organization",
func(ctx context.Context, exec bob.Executor, retrieved OrganizationLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error {
return retrieved.LoadOrganization(ctx, exec, mods...)
},
),
ResponseEmailLogEmailLog: thenLoadBuilder[Q](
"ResponseEmailLogEmailLog",
func(ctx context.Context, exec bob.Executor, retrieved ResponseEmailLogEmailLogLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error {
@ -1698,6 +2138,18 @@ func buildCommunicationThenLoader[Q orm.Loadable]() communicationThenLoader[Q] {
return retrieved.LoadSetPendingByUser(ctx, exec, mods...)
},
),
SetPossibleIssueByUser: thenLoadBuilder[Q](
"SetPossibleIssueByUser",
func(ctx context.Context, exec bob.Executor, retrieved SetPossibleIssueByUserLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error {
return retrieved.LoadSetPossibleIssueByUser(ctx, exec, mods...)
},
),
SetPossibleResolvedByUser: thenLoadBuilder[Q](
"SetPossibleResolvedByUser",
func(ctx context.Context, exec bob.Executor, retrieved SetPossibleResolvedByUserLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error {
return retrieved.LoadSetPossibleResolvedByUser(ctx, exec, mods...)
},
),
SourceEmailLogEmailLog: thenLoadBuilder[Q](
"SourceEmailLogEmailLog",
func(ctx context.Context, exec bob.Executor, retrieved SourceEmailLogEmailLogLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error {
@ -1884,6 +2336,58 @@ func (os CommunicationSlice) LoadOpenedByUser(ctx context.Context, exec bob.Exec
return nil
}
// LoadOrganization loads the communication's Organization into the .R struct
func (o *Communication) LoadOrganization(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if o == nil {
return nil
}
// Reset the relationship
o.R.Organization = nil
related, err := o.Organization(mods...).One(ctx, exec)
if err != nil {
return err
}
related.R.Communications = CommunicationSlice{o}
o.R.Organization = related
return nil
}
// LoadOrganization loads the communication's Organization into the .R struct
func (os CommunicationSlice) LoadOrganization(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if len(os) == 0 {
return nil
}
organizations, err := os.Organization(mods...).All(ctx, exec)
if err != nil {
return err
}
for _, o := range os {
if o == nil {
continue
}
for _, rel := range organizations {
if !(o.OrganizationID == rel.ID) {
continue
}
rel.R.Communications = append(rel.R.Communications, o)
o.R.Organization = rel
break
}
}
return nil
}
// LoadResponseEmailLogEmailLog loads the communication's ResponseEmailLogEmailLog into the .R struct
func (o *Communication) LoadResponseEmailLogEmailLog(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if o == nil {
@ -2049,6 +2553,116 @@ func (os CommunicationSlice) LoadSetPendingByUser(ctx context.Context, exec bob.
return nil
}
// LoadSetPossibleIssueByUser loads the communication's SetPossibleIssueByUser into the .R struct
func (o *Communication) LoadSetPossibleIssueByUser(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if o == nil {
return nil
}
// Reset the relationship
o.R.SetPossibleIssueByUser = nil
related, err := o.SetPossibleIssueByUser(mods...).One(ctx, exec)
if err != nil {
return err
}
related.R.SetPossibleIssueByCommunications = CommunicationSlice{o}
o.R.SetPossibleIssueByUser = related
return nil
}
// LoadSetPossibleIssueByUser loads the communication's SetPossibleIssueByUser into the .R struct
func (os CommunicationSlice) LoadSetPossibleIssueByUser(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if len(os) == 0 {
return nil
}
users, err := os.SetPossibleIssueByUser(mods...).All(ctx, exec)
if err != nil {
return err
}
for _, o := range os {
if o == nil {
continue
}
for _, rel := range users {
if !o.SetPossibleIssueBy.IsValue() {
continue
}
if !(o.SetPossibleIssueBy.IsValue() && o.SetPossibleIssueBy.MustGet() == rel.ID) {
continue
}
rel.R.SetPossibleIssueByCommunications = append(rel.R.SetPossibleIssueByCommunications, o)
o.R.SetPossibleIssueByUser = rel
break
}
}
return nil
}
// LoadSetPossibleResolvedByUser loads the communication's SetPossibleResolvedByUser into the .R struct
func (o *Communication) LoadSetPossibleResolvedByUser(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if o == nil {
return nil
}
// Reset the relationship
o.R.SetPossibleResolvedByUser = nil
related, err := o.SetPossibleResolvedByUser(mods...).One(ctx, exec)
if err != nil {
return err
}
related.R.SetPossibleResolvedByCommunications = CommunicationSlice{o}
o.R.SetPossibleResolvedByUser = related
return nil
}
// LoadSetPossibleResolvedByUser loads the communication's SetPossibleResolvedByUser into the .R struct
func (os CommunicationSlice) LoadSetPossibleResolvedByUser(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if len(os) == 0 {
return nil
}
users, err := os.SetPossibleResolvedByUser(mods...).All(ctx, exec)
if err != nil {
return err
}
for _, o := range os {
if o == nil {
continue
}
for _, rel := range users {
if !o.SetPossibleResolvedBy.IsValue() {
continue
}
if !(o.SetPossibleResolvedBy.IsValue() && o.SetPossibleResolvedBy.MustGet() == rel.ID) {
continue
}
rel.R.SetPossibleResolvedByCommunications = append(rel.R.SetPossibleResolvedByCommunications, o)
o.R.SetPossibleResolvedByUser = rel
break
}
}
return nil
}
// LoadSourceEmailLogEmailLog loads the communication's SourceEmailLogEmailLog into the .R struct
func (o *Communication) LoadSourceEmailLogEmailLog(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if o == nil {

View file

@ -78,6 +78,7 @@ type OrganizationsQuery = *psql.ViewQuery[*Organization, OrganizationSlice]
// organizationR is where relationships are stored.
type organizationR struct {
Communications CommunicationSlice // communication.communication_organization_id_fkey
EmailContacts CommsEmailContactSlice // district_subscription_email.district_subscription_email_email_contact_address_fkeydistrict_subscription_email.district_subscription_email_organization_id_fkey
Phones CommsPhoneSlice // district_subscription_phone.district_subscription_phone_organization_id_fkeydistrict_subscription_phone.district_subscription_phone_phone_e164_fkey
Features FeatureSlice // feature.feature_organization_id_fkey
@ -972,6 +973,30 @@ func (o OrganizationSlice) ReloadAll(ctx context.Context, exec bob.Executor) err
return nil
}
// Communications starts a query for related objects on communication
func (o *Organization) Communications(mods ...bob.Mod[*dialect.SelectQuery]) CommunicationsQuery {
return Communications.Query(append(mods,
sm.Where(Communications.Columns.OrganizationID.EQ(psql.Arg(o.ID))),
)...)
}
func (os OrganizationSlice) Communications(mods ...bob.Mod[*dialect.SelectQuery]) CommunicationsQuery {
pkID := make(pgtypes.Array[int32], 0, len(os))
for _, o := range os {
if o == nil {
continue
}
pkID = append(pkID, o.ID)
}
PKArgExpr := psql.Select(sm.Columns(
psql.F("unnest", psql.Cast(psql.Arg(pkID), "integer[]")),
))
return Communications.Query(append(mods,
sm.Where(psql.Group(Communications.Columns.OrganizationID).OP("IN", PKArgExpr)),
)...)
}
// EmailContacts starts a query for related objects on comms.email_contact
func (o *Organization) EmailContacts(mods ...bob.Mod[*dialect.SelectQuery]) CommsEmailContactsQuery {
return CommsEmailContacts.Query(append(mods,
@ -1990,6 +2015,74 @@ func (os OrganizationSlice) User(mods ...bob.Mod[*dialect.SelectQuery]) UsersQue
)...)
}
func insertOrganizationCommunications0(ctx context.Context, exec bob.Executor, communications1 []*CommunicationSetter, organization0 *Organization) (CommunicationSlice, error) {
for i := range communications1 {
communications1[i].OrganizationID = omit.From(organization0.ID)
}
ret, err := Communications.Insert(bob.ToMods(communications1...)).All(ctx, exec)
if err != nil {
return ret, fmt.Errorf("insertOrganizationCommunications0: %w", err)
}
return ret, nil
}
func attachOrganizationCommunications0(ctx context.Context, exec bob.Executor, count int, communications1 CommunicationSlice, organization0 *Organization) (CommunicationSlice, error) {
setter := &CommunicationSetter{
OrganizationID: omit.From(organization0.ID),
}
err := communications1.UpdateAll(ctx, exec, *setter)
if err != nil {
return nil, fmt.Errorf("attachOrganizationCommunications0: %w", err)
}
return communications1, nil
}
func (organization0 *Organization) InsertCommunications(ctx context.Context, exec bob.Executor, related ...*CommunicationSetter) error {
if len(related) == 0 {
return nil
}
var err error
communications1, err := insertOrganizationCommunications0(ctx, exec, related, organization0)
if err != nil {
return err
}
organization0.R.Communications = append(organization0.R.Communications, communications1...)
for _, rel := range communications1 {
rel.R.Organization = organization0
}
return nil
}
func (organization0 *Organization) AttachCommunications(ctx context.Context, exec bob.Executor, related ...*Communication) error {
if len(related) == 0 {
return nil
}
var err error
communications1 := CommunicationSlice(related)
_, err = attachOrganizationCommunications0(ctx, exec, len(related), communications1, organization0)
if err != nil {
return err
}
organization0.R.Communications = append(organization0.R.Communications, communications1...)
for _, rel := range related {
rel.R.Organization = organization0
}
return nil
}
func attachOrganizationEmailContacts0(ctx context.Context, exec bob.Executor, count int, organization0 *Organization, commsEmailContacts2 CommsEmailContactSlice) (DistrictSubscriptionEmailSlice, error) {
setters := make([]*DistrictSubscriptionEmailSetter, count)
for i := range count {
@ -4928,6 +5021,20 @@ func (o *Organization) Preload(name string, retrieved any) error {
}
switch name {
case "Communications":
rels, ok := retrieved.(CommunicationSlice)
if !ok {
return fmt.Errorf("organization cannot load %T as %q", retrieved, name)
}
o.R.Communications = rels
for _, rel := range rels {
if rel != nil {
rel.R.Organization = o
}
}
return nil
case "EmailContacts":
rels, ok := retrieved.(CommsEmailContactSlice)
if !ok {
@ -5528,6 +5635,7 @@ func buildOrganizationPreloader() organizationPreloader {
}
type organizationThenLoader[Q orm.Loadable] struct {
Communications func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
EmailContacts func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
Phones func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
Features func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
@ -5573,6 +5681,9 @@ type organizationThenLoader[Q orm.Loadable] struct {
}
func buildOrganizationThenLoader[Q orm.Loadable]() organizationThenLoader[Q] {
type CommunicationsLoadInterface interface {
LoadCommunications(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
}
type EmailContactsLoadInterface interface {
LoadEmailContacts(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
}
@ -5701,6 +5812,12 @@ func buildOrganizationThenLoader[Q orm.Loadable]() organizationThenLoader[Q] {
}
return organizationThenLoader[Q]{
Communications: thenLoadBuilder[Q](
"Communications",
func(ctx context.Context, exec bob.Executor, retrieved CommunicationsLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error {
return retrieved.LoadCommunications(ctx, exec, mods...)
},
),
EmailContacts: thenLoadBuilder[Q](
"EmailContacts",
func(ctx context.Context, exec bob.Executor, retrieved EmailContactsLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error {
@ -5956,6 +6073,67 @@ func buildOrganizationThenLoader[Q orm.Loadable]() organizationThenLoader[Q] {
}
}
// LoadCommunications loads the organization's Communications into the .R struct
func (o *Organization) LoadCommunications(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if o == nil {
return nil
}
// Reset the relationship
o.R.Communications = nil
related, err := o.Communications(mods...).All(ctx, exec)
if err != nil {
return err
}
for _, rel := range related {
rel.R.Organization = o
}
o.R.Communications = related
return nil
}
// LoadCommunications loads the organization's Communications into the .R struct
func (os OrganizationSlice) LoadCommunications(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if len(os) == 0 {
return nil
}
communications, err := os.Communications(mods...).All(ctx, exec)
if err != nil {
return err
}
for _, o := range os {
if o == nil {
continue
}
o.R.Communications = nil
}
for _, o := range os {
if o == nil {
continue
}
for _, rel := range communications {
if !(o.ID == rel.OrganizationID) {
continue
}
rel.R.Organization = o
o.R.Communications = append(o.R.Communications, rel)
}
}
return nil
}
// LoadEmailContacts loads the organization's EmailContacts into the .R struct
func (o *Organization) LoadEmailContacts(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if o == nil {

View file

@ -31,7 +31,7 @@ type PublicreportCompliance struct {
Comments string `db:"comments" `
GateCode string `db:"gate_code" `
HasDog null.Val[bool] `db:"has_dog" `
PermissionType enums.Permissionaccesstype `db:"permission_type" `
PermissionType enums.PublicreportPermissionaccesstype `db:"permission_type" `
ReportID int32 `db:"report_id,pk" `
ReportPhoneCanText null.Val[bool] `db:"report_phone_can_text" `
WantsScheduled null.Val[bool] `db:"wants_scheduled" `
@ -106,7 +106,7 @@ type PublicreportComplianceSetter struct {
Comments omit.Val[string] `db:"comments" `
GateCode omit.Val[string] `db:"gate_code" `
HasDog omitnull.Val[bool] `db:"has_dog" `
PermissionType omit.Val[enums.Permissionaccesstype] `db:"permission_type" `
PermissionType omit.Val[enums.PublicreportPermissionaccesstype] `db:"permission_type" `
ReportID omit.Val[int32] `db:"report_id,pk" `
ReportPhoneCanText omitnull.Val[bool] `db:"report_phone_can_text" `
WantsScheduled omitnull.Val[bool] `db:"wants_scheduled" `
@ -633,7 +633,7 @@ type publicreportComplianceWhere[Q psql.Filterable] struct {
Comments psql.WhereMod[Q, string]
GateCode psql.WhereMod[Q, string]
HasDog psql.WhereNullMod[Q, bool]
PermissionType psql.WhereMod[Q, enums.Permissionaccesstype]
PermissionType psql.WhereMod[Q, enums.PublicreportPermissionaccesstype]
ReportID psql.WhereMod[Q, int32]
ReportPhoneCanText psql.WhereNullMod[Q, bool]
WantsScheduled psql.WhereNullMod[Q, bool]
@ -651,7 +651,7 @@ func buildPublicreportComplianceWhere[Q psql.Filterable](cols publicreportCompli
Comments: psql.Where[Q, string](cols.Comments),
GateCode: psql.Where[Q, string](cols.GateCode),
HasDog: psql.WhereNull[Q, bool](cols.HasDog),
PermissionType: psql.Where[Q, enums.Permissionaccesstype](cols.PermissionType),
PermissionType: psql.Where[Q, enums.PublicreportPermissionaccesstype](cols.PermissionType),
ReportID: psql.Where[Q, int32](cols.ReportID),
ReportPhoneCanText: psql.WhereNull[Q, bool](cols.ReportPhoneCanText),
WantsScheduled: psql.WhereNull[Q, bool](cols.WantsScheduled),

View file

@ -65,6 +65,8 @@ type userR struct {
InvalidatedByCommunications CommunicationSlice // communication.communication_invalidated_by_fkey
OpenedByCommunications CommunicationSlice // communication.communication_opened_by_fkey
SetPendingByCommunications CommunicationSlice // communication.communication_set_pending_by_fkey
SetPossibleIssueByCommunications CommunicationSlice // communication.communication_set_possible_issue_by_fkey
SetPossibleResolvedByCommunications CommunicationSlice // communication.communication_set_possible_resolved_by_fkey
CreatorComplianceReportRequests ComplianceReportRequestSlice // compliance_report_request.compliance_report_request_creator_fkey
CreatorFeatures FeatureSlice // feature.feature_creator_id_fkey
CommitterFiles FileuploadFileSlice // fileupload.file.file_committer_fkey
@ -866,6 +868,54 @@ func (os UserSlice) SetPendingByCommunications(mods ...bob.Mod[*dialect.SelectQu
)...)
}
// SetPossibleIssueByCommunications starts a query for related objects on communication
func (o *User) SetPossibleIssueByCommunications(mods ...bob.Mod[*dialect.SelectQuery]) CommunicationsQuery {
return Communications.Query(append(mods,
sm.Where(Communications.Columns.SetPossibleIssueBy.EQ(psql.Arg(o.ID))),
)...)
}
func (os UserSlice) SetPossibleIssueByCommunications(mods ...bob.Mod[*dialect.SelectQuery]) CommunicationsQuery {
pkID := make(pgtypes.Array[int32], 0, len(os))
for _, o := range os {
if o == nil {
continue
}
pkID = append(pkID, o.ID)
}
PKArgExpr := psql.Select(sm.Columns(
psql.F("unnest", psql.Cast(psql.Arg(pkID), "integer[]")),
))
return Communications.Query(append(mods,
sm.Where(psql.Group(Communications.Columns.SetPossibleIssueBy).OP("IN", PKArgExpr)),
)...)
}
// SetPossibleResolvedByCommunications starts a query for related objects on communication
func (o *User) SetPossibleResolvedByCommunications(mods ...bob.Mod[*dialect.SelectQuery]) CommunicationsQuery {
return Communications.Query(append(mods,
sm.Where(Communications.Columns.SetPossibleResolvedBy.EQ(psql.Arg(o.ID))),
)...)
}
func (os UserSlice) SetPossibleResolvedByCommunications(mods ...bob.Mod[*dialect.SelectQuery]) CommunicationsQuery {
pkID := make(pgtypes.Array[int32], 0, len(os))
for _, o := range os {
if o == nil {
continue
}
pkID = append(pkID, o.ID)
}
PKArgExpr := psql.Select(sm.Columns(
psql.F("unnest", psql.Cast(psql.Arg(pkID), "integer[]")),
))
return Communications.Query(append(mods,
sm.Where(psql.Group(Communications.Columns.SetPossibleResolvedBy).OP("IN", PKArgExpr)),
)...)
}
// CreatorComplianceReportRequests starts a query for related objects on compliance_report_request
func (o *User) CreatorComplianceReportRequests(mods ...bob.Mod[*dialect.SelectQuery]) ComplianceReportRequestsQuery {
return ComplianceReportRequests.Query(append(mods,
@ -1806,6 +1856,142 @@ func (user0 *User) AttachSetPendingByCommunications(ctx context.Context, exec bo
return nil
}
func insertUserSetPossibleIssueByCommunications0(ctx context.Context, exec bob.Executor, communications1 []*CommunicationSetter, user0 *User) (CommunicationSlice, error) {
for i := range communications1 {
communications1[i].SetPossibleIssueBy = omitnull.From(user0.ID)
}
ret, err := Communications.Insert(bob.ToMods(communications1...)).All(ctx, exec)
if err != nil {
return ret, fmt.Errorf("insertUserSetPossibleIssueByCommunications0: %w", err)
}
return ret, nil
}
func attachUserSetPossibleIssueByCommunications0(ctx context.Context, exec bob.Executor, count int, communications1 CommunicationSlice, user0 *User) (CommunicationSlice, error) {
setter := &CommunicationSetter{
SetPossibleIssueBy: omitnull.From(user0.ID),
}
err := communications1.UpdateAll(ctx, exec, *setter)
if err != nil {
return nil, fmt.Errorf("attachUserSetPossibleIssueByCommunications0: %w", err)
}
return communications1, nil
}
func (user0 *User) InsertSetPossibleIssueByCommunications(ctx context.Context, exec bob.Executor, related ...*CommunicationSetter) error {
if len(related) == 0 {
return nil
}
var err error
communications1, err := insertUserSetPossibleIssueByCommunications0(ctx, exec, related, user0)
if err != nil {
return err
}
user0.R.SetPossibleIssueByCommunications = append(user0.R.SetPossibleIssueByCommunications, communications1...)
for _, rel := range communications1 {
rel.R.SetPossibleIssueByUser = user0
}
return nil
}
func (user0 *User) AttachSetPossibleIssueByCommunications(ctx context.Context, exec bob.Executor, related ...*Communication) error {
if len(related) == 0 {
return nil
}
var err error
communications1 := CommunicationSlice(related)
_, err = attachUserSetPossibleIssueByCommunications0(ctx, exec, len(related), communications1, user0)
if err != nil {
return err
}
user0.R.SetPossibleIssueByCommunications = append(user0.R.SetPossibleIssueByCommunications, communications1...)
for _, rel := range related {
rel.R.SetPossibleIssueByUser = user0
}
return nil
}
func insertUserSetPossibleResolvedByCommunications0(ctx context.Context, exec bob.Executor, communications1 []*CommunicationSetter, user0 *User) (CommunicationSlice, error) {
for i := range communications1 {
communications1[i].SetPossibleResolvedBy = omitnull.From(user0.ID)
}
ret, err := Communications.Insert(bob.ToMods(communications1...)).All(ctx, exec)
if err != nil {
return ret, fmt.Errorf("insertUserSetPossibleResolvedByCommunications0: %w", err)
}
return ret, nil
}
func attachUserSetPossibleResolvedByCommunications0(ctx context.Context, exec bob.Executor, count int, communications1 CommunicationSlice, user0 *User) (CommunicationSlice, error) {
setter := &CommunicationSetter{
SetPossibleResolvedBy: omitnull.From(user0.ID),
}
err := communications1.UpdateAll(ctx, exec, *setter)
if err != nil {
return nil, fmt.Errorf("attachUserSetPossibleResolvedByCommunications0: %w", err)
}
return communications1, nil
}
func (user0 *User) InsertSetPossibleResolvedByCommunications(ctx context.Context, exec bob.Executor, related ...*CommunicationSetter) error {
if len(related) == 0 {
return nil
}
var err error
communications1, err := insertUserSetPossibleResolvedByCommunications0(ctx, exec, related, user0)
if err != nil {
return err
}
user0.R.SetPossibleResolvedByCommunications = append(user0.R.SetPossibleResolvedByCommunications, communications1...)
for _, rel := range communications1 {
rel.R.SetPossibleResolvedByUser = user0
}
return nil
}
func (user0 *User) AttachSetPossibleResolvedByCommunications(ctx context.Context, exec bob.Executor, related ...*Communication) error {
if len(related) == 0 {
return nil
}
var err error
communications1 := CommunicationSlice(related)
_, err = attachUserSetPossibleResolvedByCommunications0(ctx, exec, len(related), communications1, user0)
if err != nil {
return err
}
user0.R.SetPossibleResolvedByCommunications = append(user0.R.SetPossibleResolvedByCommunications, communications1...)
for _, rel := range related {
rel.R.SetPossibleResolvedByUser = user0
}
return nil
}
func insertUserCreatorComplianceReportRequests0(ctx context.Context, exec bob.Executor, complianceReportRequests1 []*ComplianceReportRequestSetter, user0 *User) (ComplianceReportRequestSlice, error) {
for i := range complianceReportRequests1 {
complianceReportRequests1[i].Creator = omit.From(user0.ID)
@ -3608,6 +3794,34 @@ func (o *User) Preload(name string, retrieved any) error {
}
}
return nil
case "SetPossibleIssueByCommunications":
rels, ok := retrieved.(CommunicationSlice)
if !ok {
return fmt.Errorf("user cannot load %T as %q", retrieved, name)
}
o.R.SetPossibleIssueByCommunications = rels
for _, rel := range rels {
if rel != nil {
rel.R.SetPossibleIssueByUser = o
}
}
return nil
case "SetPossibleResolvedByCommunications":
rels, ok := retrieved.(CommunicationSlice)
if !ok {
return fmt.Errorf("user cannot load %T as %q", retrieved, name)
}
o.R.SetPossibleResolvedByCommunications = rels
for _, rel := range rels {
if rel != nil {
rel.R.SetPossibleResolvedByUser = o
}
}
return nil
case "CreatorComplianceReportRequests":
rels, ok := retrieved.(ComplianceReportRequestSlice)
if !ok {
@ -3989,6 +4203,8 @@ type userThenLoader[Q orm.Loadable] struct {
InvalidatedByCommunications func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
OpenedByCommunications func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
SetPendingByCommunications func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
SetPossibleIssueByCommunications func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
SetPossibleResolvedByCommunications func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
CreatorComplianceReportRequests func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
CreatorFeatures func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
CommitterFiles func(...bob.Mod[*dialect.SelectQuery]) orm.Loader[Q]
@ -4032,6 +4248,12 @@ func buildUserThenLoader[Q orm.Loadable]() userThenLoader[Q] {
type SetPendingByCommunicationsLoadInterface interface {
LoadSetPendingByCommunications(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
}
type SetPossibleIssueByCommunicationsLoadInterface interface {
LoadSetPossibleIssueByCommunications(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
}
type SetPossibleResolvedByCommunicationsLoadInterface interface {
LoadSetPossibleResolvedByCommunications(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
}
type CreatorComplianceReportRequestsLoadInterface interface {
LoadCreatorComplianceReportRequests(context.Context, bob.Executor, ...bob.Mod[*dialect.SelectQuery]) error
}
@ -4139,6 +4361,18 @@ func buildUserThenLoader[Q orm.Loadable]() userThenLoader[Q] {
return retrieved.LoadSetPendingByCommunications(ctx, exec, mods...)
},
),
SetPossibleIssueByCommunications: thenLoadBuilder[Q](
"SetPossibleIssueByCommunications",
func(ctx context.Context, exec bob.Executor, retrieved SetPossibleIssueByCommunicationsLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error {
return retrieved.LoadSetPossibleIssueByCommunications(ctx, exec, mods...)
},
),
SetPossibleResolvedByCommunications: thenLoadBuilder[Q](
"SetPossibleResolvedByCommunications",
func(ctx context.Context, exec bob.Executor, retrieved SetPossibleResolvedByCommunicationsLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error {
return retrieved.LoadSetPossibleResolvedByCommunications(ctx, exec, mods...)
},
),
CreatorComplianceReportRequests: thenLoadBuilder[Q](
"CreatorComplianceReportRequests",
func(ctx context.Context, exec bob.Executor, retrieved CreatorComplianceReportRequestsLoadInterface, mods ...bob.Mod[*dialect.SelectQuery]) error {
@ -4612,6 +4846,134 @@ func (os UserSlice) LoadSetPendingByCommunications(ctx context.Context, exec bob
return nil
}
// LoadSetPossibleIssueByCommunications loads the user's SetPossibleIssueByCommunications into the .R struct
func (o *User) LoadSetPossibleIssueByCommunications(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if o == nil {
return nil
}
// Reset the relationship
o.R.SetPossibleIssueByCommunications = nil
related, err := o.SetPossibleIssueByCommunications(mods...).All(ctx, exec)
if err != nil {
return err
}
for _, rel := range related {
rel.R.SetPossibleIssueByUser = o
}
o.R.SetPossibleIssueByCommunications = related
return nil
}
// LoadSetPossibleIssueByCommunications loads the user's SetPossibleIssueByCommunications into the .R struct
func (os UserSlice) LoadSetPossibleIssueByCommunications(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if len(os) == 0 {
return nil
}
communications, err := os.SetPossibleIssueByCommunications(mods...).All(ctx, exec)
if err != nil {
return err
}
for _, o := range os {
if o == nil {
continue
}
o.R.SetPossibleIssueByCommunications = nil
}
for _, o := range os {
if o == nil {
continue
}
for _, rel := range communications {
if !rel.SetPossibleIssueBy.IsValue() {
continue
}
if !(rel.SetPossibleIssueBy.IsValue() && o.ID == rel.SetPossibleIssueBy.MustGet()) {
continue
}
rel.R.SetPossibleIssueByUser = o
o.R.SetPossibleIssueByCommunications = append(o.R.SetPossibleIssueByCommunications, rel)
}
}
return nil
}
// LoadSetPossibleResolvedByCommunications loads the user's SetPossibleResolvedByCommunications into the .R struct
func (o *User) LoadSetPossibleResolvedByCommunications(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if o == nil {
return nil
}
// Reset the relationship
o.R.SetPossibleResolvedByCommunications = nil
related, err := o.SetPossibleResolvedByCommunications(mods...).All(ctx, exec)
if err != nil {
return err
}
for _, rel := range related {
rel.R.SetPossibleResolvedByUser = o
}
o.R.SetPossibleResolvedByCommunications = related
return nil
}
// LoadSetPossibleResolvedByCommunications loads the user's SetPossibleResolvedByCommunications into the .R struct
func (os UserSlice) LoadSetPossibleResolvedByCommunications(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if len(os) == 0 {
return nil
}
communications, err := os.SetPossibleResolvedByCommunications(mods...).All(ctx, exec)
if err != nil {
return err
}
for _, o := range os {
if o == nil {
continue
}
o.R.SetPossibleResolvedByCommunications = nil
}
for _, o := range os {
if o == nil {
continue
}
for _, rel := range communications {
if !rel.SetPossibleResolvedBy.IsValue() {
continue
}
if !(rel.SetPossibleResolvedBy.IsValue() && o.ID == rel.SetPossibleResolvedBy.MustGet()) {
continue
}
rel.R.SetPossibleResolvedByUser = o
o.R.SetPossibleResolvedByCommunications = append(o.R.SetPossibleResolvedByCommunications, rel)
}
}
return nil
}
// LoadCreatorComplianceReportRequests loads the user's CreatorComplianceReportRequests into the .R struct
func (o *User) LoadCreatorComplianceReportRequests(ctx context.Context, exec bob.Executor, mods ...bob.Mod[*dialect.SelectQuery]) error {
if o == nil {

View file

@ -18,6 +18,13 @@ func CommunicationInsert(ctx context.Context, txn bob.Tx, m *model.Communication
RETURNING(table.Communication.AllColumns)
return db.ExecuteOne[model.Communication](ctx, statement)
}
func CommunicationFromID(ctx context.Context, comm_id int64) (*model.Communication, error) {
statement := table.Communication.SELECT(
table.Communication.AllColumns,
).FROM(table.Communication).
WHERE(table.Communication.ID.EQ(postgres.Int(comm_id)))
return db.ExecuteOne[model.Communication](ctx, statement)
}
func CommunicationsFromOrganization(ctx context.Context, org_id int64) ([]*model.Communication, error) {
statement := table.Communication.SELECT(
table.Communication.AllColumns,
@ -25,3 +32,35 @@ func CommunicationsFromOrganization(ctx context.Context, org_id int64) ([]*model
WHERE(table.Communication.OrganizationID.EQ(postgres.Int(org_id)))
return db.ExecuteMany[model.Communication](ctx, statement)
}
func CommunicationMarkInvalid(ctx context.Context, org_id int64, user_id int64, comm_id int64) error {
statement := table.Communication.UPDATE().
SET(table.Communication.Invalidated.SET(postgres.LOCALTIMESTAMP())).
SET(table.Communication.InvalidatedBy.SET(postgres.Int(user_id))).
WHERE(table.Communication.OrganizationID.EQ(postgres.Int(org_id)).AND(
table.Communication.ID.EQ(postgres.Int(comm_id))))
return db.ExecuteNone(ctx, statement)
}
func CommunicationMarkPendingResponse(ctx context.Context, org_id int64, user_id int64, comm_id int64) error {
statement := table.Communication.UPDATE().
SET(table.Communication.SetPending.SET(postgres.LOCALTIMESTAMP())).
SET(table.Communication.SetPendingBy.SET(postgres.Int(user_id))).
WHERE(table.Communication.OrganizationID.EQ(postgres.Int(org_id)).AND(
table.Communication.ID.EQ(postgres.Int(comm_id))))
return db.ExecuteNone(ctx, statement)
}
func CommunicationMarkPossibleIssue(ctx context.Context, org_id int64, user_id int64, comm_id int64) error {
statement := table.Communication.UPDATE().
SET(table.Communication.SetPossibleIssue.SET(postgres.LOCALTIMESTAMP())).
SET(table.Communication.SetPossibleIssueBy.SET(postgres.Int(user_id))).
WHERE(table.Communication.OrganizationID.EQ(postgres.Int(org_id)).AND(
table.Communication.ID.EQ(postgres.Int(comm_id))))
return db.ExecuteNone(ctx, statement)
}
func CommunicationMarkPossibleResolved(ctx context.Context, org_id int64, user_id int64, comm_id int64) error {
statement := table.Communication.UPDATE().
SET(table.Communication.SetPossibleResolved.SET(postgres.LOCALTIMESTAMP())).
SET(table.Communication.SetPossibleResolvedBy.SET(postgres.Int(user_id))).
WHERE(table.Communication.OrganizationID.EQ(postgres.Int(org_id)).AND(
table.Communication.ID.EQ(postgres.Int(comm_id))))
return db.ExecuteNone(ctx, statement)
}

View file

@ -3,6 +3,7 @@ package html
import (
"bytes"
"fmt"
"github.com/Gleipnir-Technology/nidus-sync/lint"
"github.com/Gleipnir-Technology/nidus-sync/platform"
"github.com/Gleipnir-Technology/nidus-sync/platform/file"
"github.com/google/uuid"
@ -21,7 +22,7 @@ func ExtractImageUpload(headers *multipart.FileHeader) (upload platform.ImageUpl
if err != nil {
return upload, fmt.Errorf("Failed to open header: %w", err)
}
defer f.Close()
defer lint.LogOnErr(f.Close, "close headers")
file_bytes, err := io.ReadAll(f)
content_type := http.DetectContentType(file_bytes)

View file

@ -5,6 +5,8 @@ import (
"fmt"
"net/url"
"time"
"github.com/Gleipnir-Technology/nidus-sync/lint"
)
// TasksListResponse represents the response from the /api/tasks endpoint
@ -131,7 +133,7 @@ func (c *Client) ListTasks(options *TasksListOptions) (*TasksListResponse, error
if err != nil {
return nil, fmt.Errorf("Failed to request %s: %v", path, err)
}
defer resp.Body.Close()
defer lint.LogOnErr(resp.Body.Close, "close response body")
// Parse response
var tasksResponse TasksListResponse

View file

@ -4,6 +4,8 @@ import (
"encoding/json"
"fmt"
"time"
"github.com/Gleipnir-Technology/nidus-sync/lint"
)
// ProjectsResponse represents the response from the /api/projects endpoint
@ -114,7 +116,7 @@ func (c *Client) Projects() (*ProjectsResponse, error) {
if err != nil {
return nil, fmt.Errorf("Failed to GET /api/projects: %w", err)
}
defer resp.Body.Close()
defer lint.LogOnErr(resp.Body.Close, "resp.Body.Close")
// Parse response
var projects ProjectsResponse

View file

@ -4,6 +4,8 @@ import (
"encoding/json"
"fmt"
"time"
"github.com/Gleipnir-Technology/nidus-sync/lint"
)
// AnnotationRequest represents the request body for creating a draft
@ -63,7 +65,7 @@ func (c *Client) CreateAnnotation(taskID int, annotation *AnnotationRequest) (*A
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
defer resp.Body.Close()
defer lint.LogOnErr(resp.Body.Close, "close resp body")
// Parse response
var createdAnnotation Annotation

25
lint/error.go Normal file
View file

@ -0,0 +1,25 @@
package lint
import (
"context"
"github.com/rs/zerolog/log"
)
type Errorable = func() error
func LogOnErr(f Errorable, msg string) {
e := f()
if e != nil {
log.Error().Err(e).Msg(msg)
}
}
type ErrorableCtx = func(context.Context) error
func LogOnErrCtx(f ErrorableCtx, ctx context.Context, msg string) {
e := f(ctx)
if e != nil {
log.Error().Err(e).Msg(msg)
}
}

18
main.go
View file

@ -16,6 +16,7 @@ import (
"github.com/Gleipnir-Technology/nidus-sync/config"
"github.com/Gleipnir-Technology/nidus-sync/db"
"github.com/Gleipnir-Technology/nidus-sync/html"
"github.com/Gleipnir-Technology/nidus-sync/lint"
"github.com/Gleipnir-Technology/nidus-sync/llm"
"github.com/Gleipnir-Technology/nidus-sync/middleware"
"github.com/Gleipnir-Technology/nidus-sync/platform"
@ -71,7 +72,7 @@ func main() {
log.Fatal().Err(err).Msg("Failed to create sentry writer")
os.Exit(2)
}
defer sentryWriter.Close()
defer lint.LogOnErr(sentryWriter.Close, "close sentry writer")
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
log.Logger = log.Output(zerolog.MultiLevelWriter(zerolog.ConsoleWriter{Out: os.Stderr}, sentryWriter))
@ -84,8 +85,14 @@ func main() {
// Defer cleanup in reverse order - these will execute LAST (LIFO)
defer func() {
log.Info().Msg("Final cleanup")
os.Stderr.Sync()
sentryWriter.Close()
err = os.Stderr.Sync()
if err != nil {
log.Error().Err(err).Msg("sync stderr")
}
err = sentryWriter.Close()
if err != nil {
log.Error().Err(err).Msg("close sentrywriter")
}
sentry.Flush(2 * time.Second)
}()
@ -183,7 +190,10 @@ func main() {
<-signalCh
log.Info().Msg("Received shutdown signal, shutting down...")
// Ensure logs are flushed
os.Stderr.Sync()
err = os.Stderr.Sync()
if err != nil {
log.Error().Err(err).Msg("stderr sync")
}
platform.EventShutdown()
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second)

View file

@ -51,7 +51,10 @@ func init() {
var buf [12]byte
var b64 string
for len(b64) < 10 {
rand.Read(buf[:])
_, err = rand.Read(buf[:])
if err != nil {
panic("failed to rand.Read")
}
b64 = base64.StdEncoding.EncodeToString(buf[:])
b64 = strings.NewReplacer("+", "", "/", "").Replace(b64)
}

View file

@ -52,12 +52,19 @@ func init() {
}
// colorWrite
func cW(w io.Writer, useColor bool, color []byte, s string, args ...interface{}) {
func cW(w io.Writer, useColor bool, color []byte, s string, args ...interface{}) error {
if IsTTY && useColor {
w.Write(color)
_, err := w.Write(color)
if err != nil {
return fmt.Errorf("write color: %w", err)
}
}
fmt.Fprintf(w, s, args...)
if IsTTY && useColor {
w.Write(reset)
_, err := w.Write(reset)
if err != nil {
return fmt.Errorf("write color: %w", err)
}
}
return nil
}

View file

@ -8,6 +8,7 @@ import (
"os"
"time"
"github.com/Gleipnir-Technology/nidus-sync/lint"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
)
@ -66,7 +67,7 @@ func (minioClient *Client) UploadFile(bucketName string, filePath string, upload
if err != nil {
return fmt.Errorf("Failed to open file %s to upload: %v", filePath, err)
}
defer file.Close()
defer lint.LogOnErr(file.Close, "close file")
// Upload the file
_, err = minioClient.client.FPutObject(context.Background(), bucketName, uploadPath, filePath, minio.PutObjectOptions{})

View file

@ -37,6 +37,7 @@ import (
"github.com/Gleipnir-Technology/nidus-sync/db/types"
"github.com/Gleipnir-Technology/nidus-sync/debug"
"github.com/Gleipnir-Technology/nidus-sync/h3utils"
"github.com/Gleipnir-Technology/nidus-sync/lint"
"github.com/Gleipnir-Technology/nidus-sync/platform/oauth"
"github.com/aarondl/opt/omit"
"github.com/aarondl/opt/omitnull"
@ -170,7 +171,7 @@ func downloadFieldseekerSchema(ctx context.Context, fieldseekerClient *fieldseek
log.Error().Err(err).Msg("Failed to open output")
return
}
defer output.Close()
defer lint.LogOnErr(output.Close, "close schema output file")
schema, err := fieldseekerClient.SchemaRaw(ctx, uint(i))
if err != nil {
log.Error().Err(err).Msg("Failed to get schema")
@ -212,7 +213,10 @@ func generateCodeChallenge(codeVerifier string) string {
// Generate a random code verifier for PKCE
func generateCodeVerifier() string {
bytes := make([]byte, 64) // 64 bytes = 512 bits
rand.Read(bytes)
_, err := rand.Read(bytes)
if err != nil {
return ""
}
return base64.RawURLEncoding.EncodeToString(bytes)
}

View file

@ -10,3 +10,25 @@ import (
func CommunicationsForOrganization(ctx context.Context, org_id int64) ([]*model.Communication, error) {
return querypublic.CommunicationsFromOrganization(ctx, org_id)
}
func CommunicationFromID(ctx context.Context, user User, comm_id int64) (*model.Communication, error) {
comm, err := querypublic.CommunicationFromID(ctx, comm_id)
if err != nil {
return nil, err
}
if comm.OrganizationID != user.Organization.ID {
return nil, nil
}
return comm, nil
}
func CommunicationMarkInvalid(ctx context.Context, user User, comm_id int64) error {
return querypublic.CommunicationMarkInvalid(ctx, int64(user.Organization.ID), int64(user.ID), comm_id)
}
func CommunicationMarkPendingResponse(ctx context.Context, user User, comm_id int64) error {
return querypublic.CommunicationMarkPendingResponse(ctx, int64(user.Organization.ID), int64(user.ID), comm_id)
}
func CommunicationMarkPossibleIssue(ctx context.Context, user User, comm_id int64) error {
return querypublic.CommunicationMarkPossibleIssue(ctx, int64(user.Organization.ID), int64(user.ID), comm_id)
}
func CommunicationMarkPossibleResolved(ctx context.Context, user User, comm_id int64) error {
return querypublic.CommunicationMarkPossibleResolved(ctx, int64(user.Organization.ID), int64(user.ID), comm_id)
}

View file

@ -256,9 +256,12 @@ func importCSV[T any](ctx context.Context, file_id int32, parser csvParserFunc[T
return fmt.Errorf("process parsed file: %w", err)
}
file.Update(ctx, txn, &models.FileuploadFileSetter{
err = file.Update(ctx, txn, &models.FileuploadFileSetter{
Status: omit.From(enums.FileuploadFilestatustypeParsed),
})
if err != nil {
return fmt.Errorf("update: %w", err)
}
log.Info().Int32("file.ID", file.ID).Msg("Set file to parsed")
txn.Commit(ctx)
return nil

View file

@ -187,9 +187,12 @@ func parseCSVPoollist(ctx context.Context, txn bob.Tx, f *models.FileuploadFile,
missing_headers := missingRequiredHeaders(header_types)
for _, mh := range missing_headers {
errorMissingHeader(ctx, txn, c, mh)
f.Update(ctx, txn, &models.FileuploadFileSetter{
err = f.Update(ctx, txn, &models.FileuploadFileSetter{
Status: omit.From(enums.FileuploadFilestatustypeError),
})
if err != nil {
return pools, fmt.Errorf("update: %w", err)
}
return pools, nil
}
for i, header_name := range header_names {
@ -279,7 +282,11 @@ func parseCSVPoollist(ctx context.Context, txn bob.Tx, f *models.FileuploadFile,
addError(ctx, txn, c, int32(line_number), int32(i), fmt.Sprintf("'%s' is not a phone number that we recognize. Ideally it should be of the form '+12223334444'", col))
continue
}
text.EnsureInDB(ctx, txn, *phone)
err = text.EnsureInDB(ctx, txn, *phone)
if err != nil {
log.Error().Err(err).Str("phone", col).Msg("ensure in DB failure")
continue
}
setter.PropertyOwnerPhoneE164 = omitnull.From(phone.PhoneString())
case headerPoolResidentOwned:
boolValue, err := parseBool(col)
@ -294,7 +301,11 @@ func parseCSVPoollist(ctx context.Context, txn bob.Tx, f *models.FileuploadFile,
addError(ctx, txn, c, int32(line_number), int32(i), fmt.Sprintf("'%s' is not a phone number that we recognize. Ideally it should be of the form '+12223334444'", col))
continue
}
text.EnsureInDB(ctx, txn, *phone)
err = text.EnsureInDB(ctx, txn, *phone)
if err != nil {
log.Error().Err(err).Str("phone", col).Msg("ensure in DB failure")
continue
}
setter.ResidentPhoneE164 = omitnull.From(phone.PhoneString())
case headerPoolTag:
tags[header_names[i]] = col

View file

@ -23,6 +23,7 @@ import (
"github.com/Gleipnir-Technology/nidus-sync/db"
"github.com/Gleipnir-Technology/nidus-sync/db/enums"
"github.com/Gleipnir-Technology/nidus-sync/db/models"
"github.com/Gleipnir-Technology/nidus-sync/lint"
"github.com/aarondl/opt/omit"
"github.com/aarondl/opt/omitnull"
"github.com/rs/zerolog/log"
@ -67,7 +68,7 @@ func LoadTemplates() error {
if err != nil {
return fmt.Errorf("Failed to start transaction: %w", err)
}
defer tx.Rollback(ctx)
defer lint.LogOnErrCtx(tx.Rollback, ctx, "rollback")
templateByID = make(map[int32]*builtTemplate, 0)
for name, p := range all_templates {
template_id, err := templateDBID(tx, name, p)

View file

@ -91,7 +91,7 @@ func ComplianceSend(ctx context.Context, row_id int32) error {
if err != nil {
return fmt.Errorf("start txn: %w", err)
}
defer txn.Rollback(nil)
defer txn.Rollback(ctx)
mailer, err := models.CommsMailers.Insert(&models.CommsMailerSetter{
AddressID: omit.From(address.ID),
Created: omit.From(time.Now()),

View file

@ -4,6 +4,7 @@ import (
"context"
"net/http"
"slices"
"strconv"
"time"
"github.com/Gleipnir-Technology/nidus-sync/config"
@ -12,7 +13,7 @@ import (
nhttp "github.com/Gleipnir-Technology/nidus-sync/http"
"github.com/Gleipnir-Technology/nidus-sync/platform"
"github.com/google/uuid"
//"github.com/gorilla/mux"
"github.com/gorilla/mux"
//"github.com/rs/zerolog/log"
)
@ -36,6 +37,7 @@ type communication struct {
Response string `json:"response"`
Source string `json:"source"`
Type string `json:"type"`
URI string `json:"uri"`
}
func toImageURLs(m map[string][]uuid.UUID, id string) []string {
@ -49,7 +51,10 @@ func toImageURLs(m map[string][]uuid.UUID, id string) []string {
}
return urls
}
func (res *communicationR) List(ctx context.Context, r *http.Request, user platform.User, query QueryParams) ([]*communication, *nhttp.ErrorWithStatus) {
func (res *communicationR) Get(ctx context.Context, r *http.Request, user platform.User, query QueryParams) (*communication, *nhttp.ErrorWithStatus) {
return nil, nil
}
func (res *communicationR) List(ctx context.Context, r *http.Request, user platform.User, query QueryParams) ([]communication, *nhttp.ErrorWithStatus) {
comms, err := platform.CommunicationsForOrganization(ctx, int64(user.Organization.ID))
if err != nil {
return nil, nhttp.NewError("nuisance report query: %w", err)
@ -68,58 +73,19 @@ func (res *communicationR) List(ctx context.Context, r *http.Request, user platf
for _, pr := range public_reports {
public_report_id_to_report[pr.ID] = pr
}
result := make([]*communication, len(comms))
result := make([]communication, len(comms))
for i, comm := range comms {
source_uri := "unknown"
type_ := "unknown"
if comm.SourceReportID != nil {
public_report, ok := public_report_id_to_report[*comm.SourceReportID]
if !ok {
return nil, nhttp.NewError("lookup report id %d failed", comm.SourceReportID)
}
source_uri, err = reportURI(res.router, "", public_report.PublicID)
c, err := res.hydrateCommunication(*comm, public_report)
if err != nil {
return nil, nhttp.NewError("gen report URI: %w", err)
return nil, err
}
type_ = "publicreport." + public_report.ReportType.String()
} else if comm.SourceEmailLogID != nil {
source_uri, err = emailURI(res.router, *comm.SourceEmailLogID)
if err != nil {
return nil, nhttp.NewError("gen email URI: %w", err)
result[i] = c
}
type_ = "email"
} else if comm.SourceTextLogID != nil {
source_uri, err = textURI(res.router, *comm.SourceTextLogID)
if err != nil {
return nil, nhttp.NewError("gen email URI: %w", err)
}
source_uri = "text"
}
closed_by, err := userURI(res.router, comm.ClosedBy)
if err != nil {
return nil, nhttp.NewError("gen closed_by URI: %w", err)
}
opened_by, err := userURI(res.router, comm.OpenedBy)
if err != nil {
return nil, nhttp.NewError("gen opened_by URI: %w", err)
}
response, err := responseURI(res.router, comm)
if err != nil {
return nil, nhttp.NewError("gen response URI: %w", err)
}
result[i] = &communication{
Closed: comm.Closed,
ClosedBy: closed_by,
Created: comm.Created,
ID: comm.ID,
Opened: comm.Opened,
OpenedBy: opened_by,
Response: response,
Source: source_uri,
Type: type_,
}
}
_by_created := func(a, b *communication) int {
_by_created := func(a, b communication) int {
if a.Created.Equal(b.Created) {
return 0
} else if a.Created.Before(b.Created) {
@ -132,10 +98,93 @@ func (res *communicationR) List(ctx context.Context, r *http.Request, user platf
return result, nil
}
func emailURI(r *router, id int32) (string, error) {
return "fake email uri", nil
type communicationMarkRequest struct{}
func (res *communicationR) MarkInvalid(ctx context.Context, r *http.Request, user platform.User, cmr communicationMarkRequest) (communication, *nhttp.ErrorWithStatus) {
return res.markReport(ctx, r, user, platform.CommunicationMarkInvalid)
}
func responseURI(r *router, comm *modelpublic.Communication) (string, error) {
func (res *communicationR) MarkPendingResponse(ctx context.Context, r *http.Request, user platform.User, cmr communicationMarkRequest) (communication, *nhttp.ErrorWithStatus) {
return res.markReport(ctx, r, user, platform.CommunicationMarkPendingResponse)
}
func (res *communicationR) MarkPossibleIssue(ctx context.Context, r *http.Request, user platform.User, cmr communicationMarkRequest) (communication, *nhttp.ErrorWithStatus) {
return res.markReport(ctx, r, user, platform.CommunicationMarkPossibleIssue)
}
func (res *communicationR) MarkPossibleResolved(ctx context.Context, r *http.Request, user platform.User, cmr communicationMarkRequest) (communication, *nhttp.ErrorWithStatus) {
return res.markReport(ctx, r, user, platform.CommunicationMarkPossibleResolved)
}
func (res *communicationR) hydrateCommunication(comm modelpublic.Communication, public_report *modelpublicreport.Report) (communication, *nhttp.ErrorWithStatus) {
var err error
source_uri := "unknown"
type_ := "unknown"
if comm.SourceReportID != nil {
source_uri, err = reportURI(res.router, "", public_report.PublicID)
if err != nil {
return communication{}, nhttp.NewError("gen report URI: %w", err)
}
type_ = "publicreport." + public_report.ReportType.String()
} else if comm.SourceEmailLogID != nil {
source_uri, err = emailURI(*res.router, *comm.SourceEmailLogID)
if err != nil {
return communication{}, nhttp.NewError("gen email URI: %w", err)
}
type_ = "email"
} else if comm.SourceTextLogID != nil {
source_uri, err = textURI(*res.router, *comm.SourceTextLogID)
if err != nil {
return communication{}, nhttp.NewError("gen email URI: %w", err)
}
source_uri = "text"
}
closed_by, err := userURI(res.router, comm.ClosedBy)
if err != nil {
return communication{}, nhttp.NewError("gen closed_by URI: %w", err)
}
opened_by, err := userURI(res.router, comm.OpenedBy)
if err != nil {
return communication{}, nhttp.NewError("gen opened_by URI: %w", err)
}
response, err := responseURI(*res.router, comm)
if err != nil {
return communication{}, nhttp.NewError("gen response URI: %w", err)
}
uri, err := res.router.IDToURI("communication.ByIDGet", int(comm.ID))
if err != nil {
return communication{}, nhttp.NewError("gen comm uri: %w", err)
}
return communication{
Closed: comm.Closed,
ClosedBy: closed_by,
Created: comm.Created,
ID: comm.ID,
Opened: comm.Opened,
OpenedBy: opened_by,
Response: response,
Source: source_uri,
Type: type_,
URI: uri,
}, nil
}
type markFunc = func(context.Context, platform.User, int64) error
func (res *communicationR) markReport(ctx context.Context, r *http.Request, user platform.User, m markFunc) (communication, *nhttp.ErrorWithStatus) {
vars := mux.Vars(r)
report_id_str := vars["id"]
if report_id_str == "" {
return communication{}, nhttp.NewBadRequest("no id provided")
}
report_id, err := strconv.Atoi(report_id_str)
if err != nil {
return communication{}, nhttp.NewBadRequest("can't turn report ID into an int: %w", err)
}
m(ctx, user, int64(report_id))
result, err := platform.CommunicationFromID(ctx, user, int64(report_id))
if result == nil {
return communication{}, nhttp.NewUnauthorized("you are not authorized to modify communication %d", report_id)
}
return res.hydrateCommunication(*result, nil)
}
func responseURI(r router, comm modelpublic.Communication) (string, error) {
if comm.ResponseEmailLogID != nil {
return emailURI(r, *comm.ResponseEmailLogID)
} else if comm.ResponseTextLogID != nil {
@ -144,7 +193,11 @@ func responseURI(r *router, comm *modelpublic.Communication) (string, error) {
return "", nil
}
}
func textURI(r *router, id int32) (string, error) {
func emailURI(r router, id int32) (string, error) {
return "fake email uri", nil
}
func textURI(r router, id int32) (string, error) {
return "fake text uri", nil
}
func userURI(r *router, id *int32) (string, error) {

View file

@ -39,7 +39,7 @@ type publicReportComplianceForm struct {
HasDog omitnull.Val[bool] `schema:"has_dog" json:"has_dog"`
Location omit.Val[types.Location] `schema:"location" json:"location"`
MailerID omit.Val[string] `schema:"mailer_id" json:"mailer_id"`
PermissionType omit.Val[enums.Permissionaccesstype] `schema:"permission_type" json:"permission_type"`
PermissionType omit.Val[enums.PublicreportPermissionaccesstype] `schema:"permission_type" json:"permission_type"`
Reporter omit.Val[types.Contact] `schema:"reporter" json:"reporter"`
ReportPhoneCanSMS omitnull.Val[bool] `schema:"report_phone_can_text" json:"report_phone_can_text"`
Submitted omitnull.Val[time.Time] `schema:"submitted" json:"submitted"`
@ -88,7 +88,7 @@ func (res *complianceR) Create(ctx context.Context, r *http.Request, n publicRep
Comments: omit.From(""),
GateCode: omit.From(""),
HasDog: omitnull.FromPtr[bool](nil),
PermissionType: omit.From(enums.PermissionaccesstypeUnselected),
PermissionType: omit.From(enums.PublicreportPermissionaccesstypeUnselected),
//ReportID omit.Val[int32]
WantsScheduled: omitnull.FromPtr[bool](nil),
}

View file

@ -8,7 +8,7 @@
<div class="card shadow-sm h-100">
<div class="card-header bg-light pane-header">Actions</div>
<div class="card-body scroll-pane">
<div v-if="loading" class="loading">Loading...</div>
<div v-if="isLoading" class="loading">Loading...</div>
<div v-else>
<div
v-if="!selectedCommunication"
@ -25,22 +25,51 @@
class="actions-panel d-flex flex-column"
>
<div class="p-3 flex-grow-1">
<!-- Create Signal -->
<p class="text-muted mt-1">Send to planning</p>
<div class="d-grid mb-3">
<button class="btn btn-success btn-lg" @click="markSignal()">
<i class="bi bi-plus-circle me-2"></i>Mark Signal
</button>
<small class="text-muted mt-1"
>This report is useful signal</small
>
<ButtonLoading
@click="markPossibleIssue()"
:disabled="!selectedReport"
icon="bi-plus-circle"
:loading="isLoading"
text="Possible Issue"
variant="warning"
/>
</div>
<!-- Mark Invalid -->
<div class="d-grid mb-3">
<button class="btn btn-outline-danger" @click="markInvalid()">
<i class="bi bi-x-circle me-2"></i>Mark Invalid
</button>
<small class="text-muted mt-1">This report isn't useful</small>
<ButtonLoading
@click="markPossibleResolved()"
:disabled="!selectedReport"
icon="bi-x-circle"
:loading="isLoading"
text="May Be Resolved"
variant="outline-success"
/>
</div>
<hr />
<div class="d-grid mb-3">
<p class="text-muted mt-1">Resolve immediately</p>
<ButtonLoading
@click="markInvalid()"
:disabled="!selectedReport"
icon="bi-x-circle"
:loading="isLoading"
text="Invalid"
variant="outline-danger"
/>
</div>
<div class="d-grid mb-3">
<ButtonLoading
@click="markPendingResponse()"
:disabled="!selectedReport"
icon="bi-clock"
:loading="isLoading"
text="Pending Response"
variant="secondary"
/>
</div>
<hr />
@ -134,14 +163,17 @@
<script setup lang="ts">
import { ref } from "vue";
import { Communication, PublicReport, User } from "@/type/api";
import ButtonLoading from "@/components/common/ButtonLoading.vue";
import ListCardActivityLog from "@/components/ListCardActivityLog.vue";
interface Emits {
(e: "markSignal"): void;
(e: "markInvalid"): void;
(e: "markPendingResponse"): void;
(e: "markPossibleIssue"): void;
(e: "markPossibleResolved"): void;
(e: "sendMessage", message: string): void;
}
interface Props {
loading: boolean;
isLoading: boolean;
selectedCommunication: Communication | null;
selectedReport: PublicReport | undefined;
}
@ -169,8 +201,14 @@ function handleTemplateChange(event: Event) {
function markInvalid() {
emit("markInvalid");
}
function markSignal() {
emit("markSignal");
function markPendingResponse() {
emit("markPendingResponse");
}
function markPossibleResolved() {
emit("markPossibleResolved");
}
function markPossibleIssue() {
emit("markPossibleIssue");
}
function sendMessage() {
emit("sendMessage", messageText.value);

View file

@ -534,6 +534,7 @@ export interface CommunicationDTO {
id: string;
source: string;
type: string;
uri: string;
}
export class Communication {
constructor(
@ -541,6 +542,7 @@ export class Communication {
public id: string,
public source: string,
public type: string,
public uri: string,
) {}
static fromJSON(json: CommunicationDTO): Communication {
return new Communication(
@ -548,6 +550,7 @@ export class Communication {
json.id,
json.source,
json.type,
json.uri,
);
}
}

View file

@ -33,9 +33,11 @@
</template>
<template #right>
<CommunicationColumnAction
:loading="storePublicReport.loading || storeCommunication.loading"
:isLoading="storePublicReport.loading || storeCommunication.loading"
@markInvalid="markInvalid"
@markSignal="markSignal"
@markPendingResponse="markPendingResponse"
@markPossibleIssue="markPossibleIssue"
@markPossibleResolved="markPossibleResolved"
@sendMessage="sendMessage"
:selectedCommunication="selectedCommunication"
:selectedReport="selectedReport"
@ -62,6 +64,7 @@ import { computed, onMounted, ref, watch } from "vue";
import { computedAsync } from "@vueuse/core";
import maplibregl from "maplibre-gl";
import { apiClient } from "@/client";
import CommunicationColumnAction from "@/components/CommunicationColumnAction.vue";
import CommunicationColumnDetail from "@/components/CommunicationColumnDetail.vue";
import CommunicationColumnList from "@/components/CommunicationColumnList.vue";
@ -212,60 +215,32 @@ function openImageViewer(index: number) {
}
async function markInvalid() {
markReport("Invalid", "invalid");
}
async function markPendingResponse() {
markReport("Pending Response", "pending-response");
}
async function markPossibleIssue() {
markReport("Possible Issue", "possible-issue");
}
async function markPossibleResolved() {
markReport("Possibly Resolved", "possible-resolved");
}
async function markReport(title: string, status: string) {
if (selectedCommunication.value == null) {
return;
}
console.log("Marking report as invalid:", selectedCommunication.value.id);
const payload = {
reportID: selectedCommunication.value.id,
};
const response = await fetch("/api/publicreport/invalid", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
const url = `${selectedCommunication.value.uri}/${status}`;
const result = apiClient.JSONPost(url, {});
showNotification(
"Report Marked Invalid",
`Report #${selectedCommunication.value.id} has been marked as invalid`,
`Report Marked ${title}`,
`Report #${selectedCommunication.value.id} has been updated`,
);
removeCurrentFromList();
await storeCommunication.fetchAll();
}
async function markSignal() {
if (selectedCommunication.value == null) {
return;
}
console.log("Marking report as signal:", selectedCommunication.value.id);
try {
const report_id = selectedCommunication.value.id;
const payload = {
reportID: report_id,
};
removeCurrentFromList();
const response = await fetch("api/publicreport/signal", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
if (!response.ok) {
throw new Error("Failed to submit signal");
}
showNotification(
"Report Marked Signal",
`Report #${report_id} has been marked as useful signal`,
);
await storeCommunication.fetchAll();
} catch (err) {
console.error("Error creating lead:", err);
}
}
function removeCurrentFromList() {
if (storeCommunication.all == null) {
return;