2026-04-01 18:12:46 +00:00
|
|
|
package resource
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
2026-05-12 17:10:05 +00:00
|
|
|
"fmt"
|
2026-04-01 18:12:46 +00:00
|
|
|
"net/http"
|
2026-05-04 19:07:29 +00:00
|
|
|
"strconv"
|
2026-05-09 00:03:11 +00:00
|
|
|
"time"
|
2026-04-01 18:12:46 +00:00
|
|
|
|
2026-05-02 00:41:31 +00:00
|
|
|
modelpublic "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/public/model"
|
|
|
|
|
modelpublicreport "github.com/Gleipnir-Technology/nidus-sync/db/gen/nidus-sync/publicreport/model"
|
2026-04-01 18:12:46 +00:00
|
|
|
nhttp "github.com/Gleipnir-Technology/nidus-sync/http"
|
|
|
|
|
"github.com/Gleipnir-Technology/nidus-sync/platform"
|
2026-05-12 17:33:58 +00:00
|
|
|
//"github.com/rs/zerolog/log"
|
2026-04-01 18:12:46 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type communicationR struct {
|
2026-04-27 23:12:15 +00:00
|
|
|
router *router
|
2026-04-01 18:12:46 +00:00
|
|
|
}
|
|
|
|
|
|
2026-04-27 23:12:15 +00:00
|
|
|
func Communication(r *router) *communicationR {
|
2026-04-01 18:12:46 +00:00
|
|
|
return &communicationR{
|
|
|
|
|
router: r,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-12 17:10:05 +00:00
|
|
|
type communicationLogType string
|
|
|
|
|
type communicationType string
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
communicationTypeUnknown communicationType = "unknown"
|
|
|
|
|
communicationTypeReportCompliance = "publicreport.compliance"
|
|
|
|
|
communicationTypeReportNuisance = "publicreport.nuisance"
|
|
|
|
|
communicationTypeReportWater = "publicreport.water"
|
|
|
|
|
)
|
|
|
|
|
|
2026-05-07 10:39:17 +00:00
|
|
|
type communicationLog struct {
|
2026-05-12 17:10:05 +00:00
|
|
|
Created time.Time `json:"created"`
|
|
|
|
|
ID string `json:"id"`
|
|
|
|
|
Type communicationLogType `json:"type"`
|
|
|
|
|
User string `json:"user"`
|
2026-05-07 10:39:17 +00:00
|
|
|
}
|
2026-04-01 18:12:46 +00:00
|
|
|
type communication struct {
|
2026-05-09 00:03:11 +00:00
|
|
|
Created time.Time `json:"created"`
|
2026-05-07 10:39:17 +00:00
|
|
|
ID string `json:"id"`
|
|
|
|
|
Log []communicationLog `json:"log"`
|
2026-05-12 17:10:05 +00:00
|
|
|
Related []resourceStub `json:"related"`
|
2026-05-07 10:39:17 +00:00
|
|
|
Response string `json:"response"`
|
|
|
|
|
Source string `json:"source"`
|
|
|
|
|
Status string `json:"status"`
|
2026-05-12 17:10:05 +00:00
|
|
|
Type communicationType `json:"type"`
|
2026-05-07 10:39:17 +00:00
|
|
|
URI string `json:"uri"`
|
2026-04-01 18:12:46 +00:00
|
|
|
}
|
2026-05-12 17:10:05 +00:00
|
|
|
type resourceType string
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
resourceTypeUnknown resourceType = "unknown"
|
|
|
|
|
resourceTypeEmail = "email"
|
|
|
|
|
resourceTypeReportCompliance = "publicreport.compliance"
|
|
|
|
|
resourceTypeReportNuisance = "publicreport.nuisance"
|
|
|
|
|
resourceTypeReportWater = "publicreport.water"
|
|
|
|
|
resourceTypeText = "text"
|
|
|
|
|
)
|
|
|
|
|
|
2026-05-09 01:39:08 +00:00
|
|
|
type resourceStub struct {
|
2026-05-12 17:10:05 +00:00
|
|
|
Created time.Time `json:"created"`
|
|
|
|
|
Type resourceType `json:"type"`
|
|
|
|
|
URI string `json:"uri"`
|
2026-05-09 01:39:08 +00:00
|
|
|
}
|
2026-04-01 18:12:46 +00:00
|
|
|
|
2026-05-11 22:43:04 +00:00
|
|
|
func (res *communicationR) Get(ctx context.Context, r *http.Request, user platform.User, query QueryParams) (communication, *nhttp.ErrorWithStatus) {
|
2026-05-12 17:10:05 +00:00
|
|
|
comm_id, error_with_status := res.router.IDFromMux(r)
|
2026-05-11 22:43:04 +00:00
|
|
|
if error_with_status != nil {
|
|
|
|
|
return communication{}, error_with_status
|
|
|
|
|
}
|
|
|
|
|
return res.hydratedCommunicationFromID(ctx, user, int32(comm_id))
|
2026-05-04 19:07:29 +00:00
|
|
|
}
|
2026-05-12 17:10:05 +00:00
|
|
|
func (res *communicationR) List(ctx context.Context, r *http.Request, user platform.User, query QueryParams) ([]communication, *nhttp.ErrorWithStatus) {
|
2026-05-01 20:49:37 +00:00
|
|
|
comms, err := platform.CommunicationsForOrganization(ctx, int64(user.Organization.ID))
|
2026-04-01 18:12:46 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, nhttp.NewError("nuisance report query: %w", err)
|
|
|
|
|
}
|
2026-05-01 20:49:37 +00:00
|
|
|
report_ids := make([]int64, 0)
|
|
|
|
|
for _, comm := range comms {
|
|
|
|
|
if comm.SourceReportID != nil {
|
|
|
|
|
report_ids = append(report_ids, int64(*comm.SourceReportID))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public_reports, err := platform.PublicReportsFromIDs(ctx, report_ids)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nhttp.NewError("public reports from IDs: %w", err)
|
|
|
|
|
}
|
2026-05-07 10:39:17 +00:00
|
|
|
public_report_id_to_report := make(map[int32]modelpublicreport.Report, 0)
|
2026-05-01 20:49:37 +00:00
|
|
|
for _, pr := range public_reports {
|
|
|
|
|
public_report_id_to_report[pr.ID] = pr
|
|
|
|
|
}
|
2026-05-12 17:10:05 +00:00
|
|
|
result := make([]communication, len(comms))
|
2026-05-01 20:49:37 +00:00
|
|
|
for i, comm := range comms {
|
2026-05-04 19:07:29 +00:00
|
|
|
public_report, ok := public_report_id_to_report[*comm.SourceReportID]
|
|
|
|
|
if !ok {
|
|
|
|
|
return nil, nhttp.NewError("lookup report id %d failed", comm.SourceReportID)
|
2026-05-02 00:41:31 +00:00
|
|
|
}
|
2026-05-12 17:10:05 +00:00
|
|
|
related_records, err := platform.CommunicationRelatedRecords(ctx, user, &comm)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nhttp.NewError("related records: %w", err)
|
|
|
|
|
}
|
|
|
|
|
c, e := res.hydrateCommunication(comm, related_records, &public_report)
|
2026-05-11 21:40:57 +00:00
|
|
|
if e != nil {
|
|
|
|
|
return nil, e
|
2026-04-01 18:12:46 +00:00
|
|
|
}
|
2026-05-04 19:07:29 +00:00
|
|
|
result[i] = c
|
2026-04-01 18:12:46 +00:00
|
|
|
}
|
2026-05-01 20:49:37 +00:00
|
|
|
return result, nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-04 19:39:03 +00:00
|
|
|
type communicationMarkRequest struct{}
|
|
|
|
|
|
2026-05-04 19:07:29 +00:00
|
|
|
func (res *communicationR) MarkInvalid(ctx context.Context, r *http.Request, user platform.User, cmr communicationMarkRequest) (communication, *nhttp.ErrorWithStatus) {
|
2026-05-04 20:30:36 +00:00
|
|
|
return res.markCommunication(ctx, r, user, "invalid", platform.CommunicationMarkInvalid)
|
2026-05-04 19:07:29 +00:00
|
|
|
}
|
|
|
|
|
func (res *communicationR) MarkPendingResponse(ctx context.Context, r *http.Request, user platform.User, cmr communicationMarkRequest) (communication, *nhttp.ErrorWithStatus) {
|
2026-05-04 20:30:36 +00:00
|
|
|
return res.markCommunication(ctx, r, user, "pending-response", platform.CommunicationMarkPendingResponse)
|
2026-05-04 19:07:29 +00:00
|
|
|
}
|
|
|
|
|
func (res *communicationR) MarkPossibleIssue(ctx context.Context, r *http.Request, user platform.User, cmr communicationMarkRequest) (communication, *nhttp.ErrorWithStatus) {
|
2026-05-04 20:30:36 +00:00
|
|
|
return res.markCommunication(ctx, r, user, "possible-issue", platform.CommunicationMarkPossibleIssue)
|
2026-05-04 19:07:29 +00:00
|
|
|
}
|
|
|
|
|
func (res *communicationR) MarkPossibleResolved(ctx context.Context, r *http.Request, user platform.User, cmr communicationMarkRequest) (communication, *nhttp.ErrorWithStatus) {
|
2026-05-04 20:30:36 +00:00
|
|
|
return res.markCommunication(ctx, r, user, "possible-resolved", platform.CommunicationMarkPossibleResolved)
|
2026-05-04 19:07:29 +00:00
|
|
|
}
|
2026-05-12 17:10:05 +00:00
|
|
|
func (res *communicationR) hydrateCommunication(comm modelpublic.Communication, related_records []platform.RelatedRecord, public_report *modelpublicreport.Report) (communication, *nhttp.ErrorWithStatus) {
|
2026-05-04 19:07:29 +00:00
|
|
|
var err error
|
|
|
|
|
source_uri := "unknown"
|
2026-05-12 17:10:05 +00:00
|
|
|
type_ := communicationTypeUnknown
|
2026-05-04 20:30:36 +00:00
|
|
|
if comm.SourceReportID != nil && public_report != nil {
|
2026-05-04 19:07:29 +00:00
|
|
|
source_uri, err = reportURI(res.router, "", public_report.PublicID)
|
|
|
|
|
if err != nil {
|
2026-05-12 17:10:05 +00:00
|
|
|
return communication{}, nhttp.NewError("gen report URI: %w", err)
|
|
|
|
|
}
|
|
|
|
|
switch public_report.ReportType {
|
|
|
|
|
case modelpublicreport.Reporttype_Compliance:
|
|
|
|
|
type_ = communicationTypeReportCompliance
|
|
|
|
|
case modelpublicreport.Reporttype_Nuisance:
|
|
|
|
|
type_ = communicationTypeReportNuisance
|
|
|
|
|
case modelpublicreport.Reporttype_Water:
|
|
|
|
|
type_ = communicationTypeReportWater
|
|
|
|
|
default:
|
|
|
|
|
type_ = communicationTypeUnknown
|
2026-05-04 19:07:29 +00:00
|
|
|
}
|
|
|
|
|
} else if comm.SourceEmailLogID != nil {
|
|
|
|
|
source_uri, err = emailURI(*res.router, *comm.SourceEmailLogID)
|
|
|
|
|
if err != nil {
|
2026-05-12 17:10:05 +00:00
|
|
|
return communication{}, nhttp.NewError("gen email URI: %w", err)
|
2026-05-04 19:07:29 +00:00
|
|
|
}
|
|
|
|
|
type_ = "email"
|
|
|
|
|
} else if comm.SourceTextLogID != nil {
|
|
|
|
|
source_uri, err = textURI(*res.router, *comm.SourceTextLogID)
|
|
|
|
|
if err != nil {
|
2026-05-12 17:10:05 +00:00
|
|
|
return communication{}, nhttp.NewError("gen email URI: %w", err)
|
2026-05-04 19:07:29 +00:00
|
|
|
}
|
|
|
|
|
source_uri = "text"
|
|
|
|
|
}
|
|
|
|
|
uri, err := res.router.IDToURI("communication.ByIDGet", int(comm.ID))
|
|
|
|
|
if err != nil {
|
2026-05-12 17:10:05 +00:00
|
|
|
return communication{}, nhttp.NewError("gen comm uri: %w", err)
|
|
|
|
|
}
|
|
|
|
|
related := make([]resourceStub, len(related_records))
|
|
|
|
|
for i, rr := range related_records {
|
|
|
|
|
var uri string
|
|
|
|
|
var r_type resourceType
|
|
|
|
|
switch rr.Type {
|
|
|
|
|
case platform.RelatedRecordTypeEmail:
|
|
|
|
|
r_type = resourceTypeEmail
|
2026-05-12 17:30:38 +00:00
|
|
|
uri, err = res.router.IDStrToURI("email.ByIDGet", rr.ID)
|
2026-05-12 17:10:05 +00:00
|
|
|
case platform.RelatedRecordTypeReportCompliance:
|
|
|
|
|
r_type = resourceTypeReportCompliance
|
2026-05-12 17:30:38 +00:00
|
|
|
uri, err = res.router.IDStrToURI("publicreport.compliance.ByIDGet", rr.ID)
|
2026-05-12 17:10:05 +00:00
|
|
|
case platform.RelatedRecordTypeReportNuisance:
|
|
|
|
|
r_type = resourceTypeReportNuisance
|
2026-05-12 17:30:38 +00:00
|
|
|
uri, err = res.router.IDStrToURI("publicreport.nuisance.ByIDGet", rr.ID)
|
2026-05-12 17:10:05 +00:00
|
|
|
case platform.RelatedRecordTypeReportWater:
|
|
|
|
|
r_type = resourceTypeReportWater
|
2026-05-12 17:30:38 +00:00
|
|
|
uri, err = res.router.IDStrToURI("publicreport.water.ByIDGet", rr.ID)
|
2026-05-12 17:10:05 +00:00
|
|
|
case platform.RelatedRecordTypeText:
|
|
|
|
|
r_type = resourceTypeText
|
2026-05-12 17:30:38 +00:00
|
|
|
uri, err = res.router.IDStrToURI("text.ByIDGet", rr.ID)
|
2026-05-12 17:10:05 +00:00
|
|
|
default:
|
|
|
|
|
r_type = resourceTypeUnknown
|
|
|
|
|
err = fmt.Errorf("unrecognized related record type '%s'", rr.Type)
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
return communication{}, nhttp.NewError("related record hydration: %w", err)
|
|
|
|
|
}
|
|
|
|
|
related[i] = resourceStub{
|
|
|
|
|
Created: rr.Created,
|
|
|
|
|
Type: r_type,
|
|
|
|
|
URI: uri,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
response, err := responseURI(*res.router, comm)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return communication{}, nhttp.NewError("gen response URI: %w", err)
|
|
|
|
|
}
|
|
|
|
|
return communication{
|
|
|
|
|
Created: comm.Created,
|
|
|
|
|
ID: strconv.Itoa(int(comm.ID)),
|
2026-05-12 23:20:54 +00:00
|
|
|
Log: []communicationLog{},
|
2026-05-12 17:10:05 +00:00
|
|
|
Related: related,
|
|
|
|
|
Response: response,
|
|
|
|
|
Source: source_uri,
|
|
|
|
|
Status: comm.Status.String(),
|
|
|
|
|
Type: type_,
|
|
|
|
|
URI: uri,
|
2026-05-04 19:07:29 +00:00
|
|
|
}, nil
|
2026-05-01 20:49:37 +00:00
|
|
|
}
|
2026-05-04 19:07:29 +00:00
|
|
|
|
2026-05-11 22:43:04 +00:00
|
|
|
func (res *communicationR) hydratedCommunicationFromID(ctx context.Context, user platform.User, comm_id int32) (communication, *nhttp.ErrorWithStatus) {
|
2026-05-04 20:30:36 +00:00
|
|
|
result, err := platform.CommunicationFromID(ctx, user, int64(comm_id))
|
2026-05-04 19:07:29 +00:00
|
|
|
if result == nil {
|
2026-05-04 20:30:36 +00:00
|
|
|
return communication{}, nhttp.NewUnauthorized("you are not authorized to modify communication %d", comm_id)
|
2026-05-04 19:07:29 +00:00
|
|
|
}
|
2026-05-11 22:43:04 +00:00
|
|
|
comm, err := platform.CommunicationFromID(ctx, user, int64(comm_id))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return communication{}, nhttp.NewError("comm from ID: %w", err)
|
|
|
|
|
}
|
2026-05-07 10:39:17 +00:00
|
|
|
var public_report modelpublicreport.Report
|
2026-05-11 22:43:04 +00:00
|
|
|
if comm.SourceReportID != nil {
|
|
|
|
|
comm_ids := []int64{int64(*comm.SourceReportID)}
|
2026-05-04 20:30:36 +00:00
|
|
|
public_reports, err := platform.PublicReportsFromIDs(ctx, comm_ids)
|
|
|
|
|
if err != nil {
|
2026-05-11 22:43:04 +00:00
|
|
|
return communication{}, nhttp.NewError("Get report %d: %w", *comm.SourceReportID, err)
|
2026-05-04 20:30:36 +00:00
|
|
|
}
|
|
|
|
|
public_report = public_reports[0]
|
|
|
|
|
}
|
2026-05-12 17:10:05 +00:00
|
|
|
related_records, err := platform.CommunicationRelatedRecords(ctx, user, comm)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return communication{}, nhttp.NewError("related records: %w", err)
|
|
|
|
|
}
|
2026-05-04 20:30:36 +00:00
|
|
|
|
2026-05-12 17:10:05 +00:00
|
|
|
return res.hydrateCommunication(*comm, related_records, &public_report)
|
2026-05-11 22:43:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type markFunc = func(context.Context, platform.User, int32) error
|
|
|
|
|
|
|
|
|
|
func (res *communicationR) markCommunication(ctx context.Context, r *http.Request, user platform.User, status string, m markFunc) (communication, *nhttp.ErrorWithStatus) {
|
2026-05-12 17:10:05 +00:00
|
|
|
comm_id, err_with_status := res.router.IDFromMux(r)
|
2026-05-11 22:43:04 +00:00
|
|
|
if err_with_status != nil {
|
|
|
|
|
return communication{}, err_with_status
|
|
|
|
|
}
|
|
|
|
|
err := m(ctx, user, int32(comm_id))
|
2026-05-11 22:00:17 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return communication{}, nhttp.NewError("failed to mark %d: %w", comm_id, err)
|
|
|
|
|
}
|
2026-05-11 22:43:04 +00:00
|
|
|
return res.hydratedCommunicationFromID(ctx, user, int32(comm_id))
|
|
|
|
|
}
|
2026-05-04 19:07:29 +00:00
|
|
|
func responseURI(r router, comm modelpublic.Communication) (string, error) {
|
2026-05-02 00:41:31 +00:00
|
|
|
if comm.ResponseEmailLogID != nil {
|
|
|
|
|
return emailURI(r, *comm.ResponseEmailLogID)
|
|
|
|
|
} else if comm.ResponseTextLogID != nil {
|
|
|
|
|
return textURI(r, *comm.ResponseTextLogID)
|
|
|
|
|
} else {
|
|
|
|
|
return "", nil
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-05-04 19:07:29 +00:00
|
|
|
func emailURI(r router, id int32) (string, error) {
|
|
|
|
|
return "fake email uri", nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func textURI(r router, id int32) (string, error) {
|
2026-05-01 20:49:37 +00:00
|
|
|
return "fake text uri", nil
|
2026-04-01 18:12:46 +00:00
|
|
|
}
|