pgmodels

package
v0.0.0-...-5f8c1fa Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 13, 2023 License: BSD-2-Clause Imports: 18 Imported by: 0

Documentation

Index

Constants

View Source
const (
	ErrAlertInstitutionID = "InstitutionID is required."
	ErrAlertType          = "Alert type is missing or invalid."
	ErrAlertContent       = "Alert content cannot be empty."
)
View Source
const (
	ErrDeletionInstitutionID = "Deletion request requires institution id."
	ErrDeletionRequesterID   = "Deletion request requires requester id."
	ErrDeletionWrongInst     = "Deletion request user belongs to wrong institution."
	ErrDeletionWrongRole     = "Deletion confirmer/canceller must be institutional admin."
	ErrDeletionUserNotFound  = "User does not exist."
	ErrDeletionUserInactive  = "User has been deactivated."
	ErrTokenNotEncrypted     = "Token must be encrypted."
	ErrDeletionIllegalObject = "User cannot delete files or objects belonging to other institutions."
	ErrDeletionBadAdmin      = "Admin cannot confirm deletion they requested. This must be approved by a second admin."
	ErrDeletionBadQuery      = "Cannot get admin list for this institution."
)
View Source
const (
	ErrInstName       = "Name must contain 5-100 characters."
	ErrInstIdentifier = "Identifier must be a domain name."
	ErrInstState      = "State must be 'A' or 'D'."
	ErrInstType       = "Please choose an institution type."
	ErrInstReceiving  = "Receiving bucket name is not valid."
	ErrInstRestore    = "Restoration bucket name is not valid."
	ErrInstMemberID   = "Please choose a parent institution."
)
View Source
const (
	TypeInsert xactType = iota
	TypeUpdate
)
View Source
const (
	ErrStorageOptionProvider = "Provider is required."
	ErrStorageOptionService  = "Service is required."
	ErrStorageOptionRegion   = "Region is required."
	ErrStorageOptionName     = "Name is required."
	ErrStorageOptionCost     = "Cost is required."
	ErrStorageOptionComment  = "Comment is required."
)
View Source
const (
	ErrUserName         = "Name must contain at least 2 characters."
	ErrUserEmail        = "Email address is required."
	ErrUserPhone        = "Please enter a phone number in format +2125551212."
	ErrUser2Factor      = "Please choose yes or no."
	ErrUserGracePeriod  = "Enter a date specifying when this user must enable two-factor authentication."
	ErrUserInst         = "Please choose an institution."
	ErrUserRole         = "Please choose a role for this user."
	ErrUserInstNotFound = "Internal error: cannot find institution."
	ErrUserInvalidAdmin = "Sys Admin role is not valid for this institution."
	ErrUserPwdMissing   = "Encrypted password is missing."
	ErrUserPwdIncorrect = "Incorrect Password."
)
View Source
const (
	ErrItemName          = "Name is required."
	ErrItemETag          = "ETag is required (32-40 bytes)."
	ErrItemBagDate       = "BagDate is required."
	ErrItemBucket        = "Bucket is required."
	ErrItemUser          = "User must be a valid email address."
	ErrItemInstID        = "InstitutionID is required."
	ErrItemDateProcessed = "DateProcessed is required."
	ErrItemNote          = "Note cannot be empty."
	ErrItemAction        = "Action is missing or invalid."
	ErrItemStage         = "Stage is missing or invalid."
	ErrItemStatus        = "Status is missing or invalid."
	ErrItemOutcome       = "Outcome cannot be empty."
)

Variables

View Source
var AlertFilters = []string{
	"created_at__gteq",
	"created_at__lteq",
	"institution_id",
	"type",
	"user_id",
}
View Source
var ChecksumFilters = []string{
	"algorithm",
	"datetime__gteq",
	"datetime__lteq",
	"digest",
	"generic_file_id",
	"generic_file_identifier",
	"institution_id",
	"intellectual_object_id",
	"state",
}
View Source
var DeletionRequestFilters = []string{
	"institution_id",
	"requested_at__gteq",
	"requested_at__lteq",
	"stage",
	"status",
}
View Source
var DepositStatsFilters = []string{
	"chart_metric",
	"end_date",
	"institution_id",
	"report_type",
	"start_date",
	"storage_option",
}

Note: chart_metric and report_type are ignored by backend. Used only in front-end.

View Source
var GenericFileCountFilters = []string{
	"institution_id",
	"state",
}
View Source
var GenericFileFilters = []string{
	"identifier",
	"uuid",
	"intellectual_object_id",
	"institution_id",
	"state",
	"storage_option",
	"storage_option__in",
	"size__gteq",
	"size__lteq",
	"created_at__gteq",
	"created_at__lteq",
	"updated_at__gteq",
	"updated_at__lteq",
	"last_fixity_check__gteq",
	"last_fixity_check__lteq",
}
View Source
var InstitutionFilters = []string{
	"name__contains",
	"type",
}
View Source
var IntellectualObjectCountFilters = []string{
	"institution_id",
	"state",
}
View Source
var IntellectualObjectFilters = []string{
	"access",
	"alt_identifier",
	"alt_identifier__starts_with",
	"bag_group_identifier",
	"bag_group_identifier__starts_with",
	"bag_name",
	"bagit_profile_identifier",
	"created_at__lteq",
	"created_at__gteq",
	"etag",
	"file_count__gteq",
	"file_count__lteq",
	"identifier",
	"institution_id",
	"institution_parent_id",
	"internal_sender_description",
	"internal_sender_identifier",
	"size__gteq",
	"size__lteq",
	"source_organization",
	"state",
	"storage_option",

	"updated_at__gteq",
	"updated_at__lteq",
}

IntellectualObjectFilters describes the allowed filters for searching IntellectualObjects. Some of these are commented out because, while they are supported in Pharo's v2 member API, we want to phase them out. "Contains" queries, in particular, which use SQL "like" on the backend, cause performance problems.

View Source
var PremisEventCountFilters = []string{
	"institution_id",
	"event_type",
	"outcome",
}
View Source
var PremisEventFilters = []string{
	"date_time__gteq",
	"date_time__lteq",
	"event_type",
	"generic_file_id",
	"generic_file_id__is_null",
	"generic_file_identifier",
	"identifier",
	"institution_id",
	"intellectual_object_id",
	"intellectual_object_identifier",
	"outcome",
}
View Source
var QueryOp = map[string]string{
	"eq":          "=",
	"ne":          "!=",
	"gt":          ">",
	"gteq":        ">=",
	"lt":          "<",
	"lteq":        "<=",
	"starts_with": "ILIKE",
	"contains":    "ILIKE",
	"in":          "IN",
	"not_in":      "NOT IN",
	"is_null":     "IS NULL",
	"not_null":    "IS NOT NULL",
}
View Source
var StorageRecordFilters = []string{
	"generic_file_id",
}
View Source
var UserFilters = []string{
	"deactivated_at__is_null",
	"deactivated_at__is_not_null",
	"email__contains",
	"institution_id",
	"name__contains",
	"role",
}
View Source
var WorkItemCountFilters = []string{
	"institution_id",
	"action",
}
View Source
var WorkItemFilters = []string{
	"action",
	"action__in",
	"alt_identifier",
	"bag_date__gteq",
	"bag_date__lteq",
	"bag_group_identifier",
	"bagit_profile_identifier",
	"bucket",
	"date_processed__gteq",
	"date_processed__lteq",
	"etag",
	"generic_file_id",
	"generic_file_id__is_null",
	"generic_file_identifier",
	"institution_id",
	"intellectual_object_id",
	"intellectual_object_id__is_null",
	"name",
	"needs_admin_review",
	"node__is_null",
	"node__not_null",
	"object_identifier",
	"queued_at__is_null",
	"queued_at__not_null",
	"retry",
	"size__gteq",
	"size__lteq",
	"stage",
	"stage__in",
	"status",
	"status__in",
	"storage_option",
	"user",
}

Functions

func BagName

func BagName() string

func CanCountFromView

func CanCountFromView(query *Query, model interface{}) bool

func ETag

func ETag() string

func FileIdentifier

func FileIdentifier(objIdentifier string) string

func FiltersFor

func FiltersFor(typeName string) []string

func GenericFileCreateBatch

func GenericFileCreateBatch(files []*GenericFile) error

GenericFileCreateBatch creates a batch of GenericFiles and their dependent records (PremisEvents, Checksums, and StorageRecords) in a single transaction. This transaction's single commit is much more efficient than doing one commit per insert.

This is used heavily during ingest.

func GetCountFromView

func GetCountFromView(query *Query, model interface{}) (int, error)

GetCountFromView returns a snapshotted count from a materialized view. We do this for some queries that are known to return very large counts, which take a long time in postgres.

Ideally, this should return int64, in line with our general practice of using int64. However, it has to be compatible with the pg library's built-in Count() function, which returns int.

func IdForEventIdentifier

func IdForEventIdentifier(identifier string) (int64, error)

IdForEventIdentifier returns the ID of the PremisEvent having the specified identifier. These identifiers are UUID strings.

func IdForFileIdentifier

func IdForFileIdentifier(identifier string) (int64, error)

IdForFileIdentifier returns the ID of the GenericFile having the specified identifier.

func IdForInstIdentifier

func IdForInstIdentifier(identifier string) (int64, error)

IdForInstIdentifier returns the id of the insitution with the given identifier, or an error if no matching record exists.

func IdForObjIdentifier

func IdForObjIdentifier(identifier string) (int64, error)

IdForFileIdentifier returns the ID of the IntellectualObject having the specified identifier.

func InstIDFor

func InstIDFor(resourceType string, resourceID int64) (id int64, err error)

func IsNoRowError

func IsNoRowError(err error) bool

IsNoRowError returns true if err is pg.ErrNoRows. For some reason, err doesn't compare correctly with pg.ErrNoRows, and errors.Is() doesn't work either. Probably because pg.ErrNoRows is an alias of an error in the pg/internal package, which we cannot access.

func ObjIdentifier

func ObjIdentifier() string

func ObjectEventCount

func ObjectEventCount(intellectualObjectID int64) (int, error)

ObjectEventCount returns the number of object-level PremisEvents for the specified IntellectualObject. Note that queries on the premis_events table can be potentially quite expensive, since the table has well over 100M rows.

Object-level events include ingest, identifier assignment, access assignment and others. They exclude all events related to specific files (such as fixity check, etc.).

func ObjectFileCount

func ObjectFileCount(objID int64, filter, state string) (int, error)

ObjectFileCount returns the number of active files with the specified Intellectial Object ID.

func RandomFileBatch

func RandomFileBatch() (*IntellectualObject, []*GenericFile, error)

RandFileBatch returns a slice of 20 GenericFiles, each with 4 events, checksums, and storage records. Note that this also creates the parent object in the database. You may want to reset the DB after calling this.

func Title

func Title() string

Types

type Alert

type Alert struct {
	BaseModel
	InstitutionID     int64            `json:"institution_id"`
	Type              string           `json:"type"`
	Subject           string           `json:"subject"`
	Content           string           `json:"content"`
	DeletionRequestID int64            `json:"deletion_request_id"`
	CreatedAt         time.Time        `json:"created_at"`
	DeletionRequest   *DeletionRequest `json:"-" pg:"rel:has-one"`
	PremisEvents      []*PremisEvent   `json:"premis_events" pg:"many2many:alerts_premis_events"`
	Users             []*User          `json:"users" pg:"many2many:alerts_users"`
	WorkItems         []*WorkItem      `json:"work_items" pg:"many2many:alerts_work_items"`
}

func AlertByID

func AlertByID(id int64) (*Alert, error)

AlertByID returns the alert with the specified id. Returns pg.ErrNoRows if there is no match.

func AlertGet

func AlertGet(query *Query) (*Alert, error)

AlertGet returns the first alert matching the query.

func AlertSelect

func AlertSelect(query *Query) ([]*Alert, error)

AlertSelect returns all alerts matching the query.

func CreateAlert

func CreateAlert(alert *Alert, templateName string, alertData map[string]interface{}) (*Alert, error)

CreateAlert adds customized text to the alert and saves it in the database. Param templateName is the name of the text template used to construct the alert message. Param alertData is the custom data to put into the template.

This returns the alert with a non-zero ID (since it saves it) and an error if there's a problem with the template or the save.

func (*Alert) MarkAsRead

func (alert *Alert) MarkAsRead(userID int64) error

MarkAsRead marks an alert as read if its current ReadAt date is null.

func (*Alert) MarkAsSent

func (alert *Alert) MarkAsSent(userID int64) error

MarkAsSent marks an alert as sent.

func (*Alert) MarkAsUnread

func (alert *Alert) MarkAsUnread(userID int64) error

MarkAsUnread marks an alert as unread.

func (*Alert) Save

func (alert *Alert) Save() error

Save saves this alert to the database. This will peform an insert if Alert.ID is zero. Otherwise, it updates. It also saves all of the many-to-many relations (PremisEvents, Users, and WorkItems), though note that on update it does not delete any of these relations. We don't have a use case for that yet, since alerts are generally created and never updated.

func (*Alert) Validate

func (alert *Alert) Validate() *common.ValidationError

Validate validates the model. This is called automatically on insert and update.

type AlertView

type AlertView struct {
	ID                    int64     `json:"id"`
	InstitutionID         int64     `json:"institution_id"`
	InstitutionName       string    `json:"institution_name"`
	InstitutionIdentifier string    `json:"institution_identifier"`
	Type                  string    `json:"type"`
	Subject               string    `json:"subject"`
	Content               string    `json:"content"`
	DeletionRequestID     int64     `json:"deletion_request_id"`
	CreatedAt             time.Time `json:"created_at"`
	UserID                int64     `json:"user_id"`
	UserName              string    `json:"user_name"`
	UserEmail             string    `json:"user_email"`
	SentAt                time.Time `json:"sent_at"`
	ReadAt                time.Time `json:"read_at"`
	// contains filtered or unexported fields
}

func AlertViewForUser

func AlertViewForUser(alertID, recipientID int64) (*AlertView, error)

AlertViewForUser returns the alert with the specified ID for the specified recipient (user id). Returns pg.ErrNoRows if there is no match.

func AlertViewGet

func AlertViewGet(query *Query) (*AlertView, error)

AlertViewGet returns the first alert matching the query.

func AlertViewSelect

func AlertViewSelect(query *Query) ([]*AlertView, error)

AlertViewSelect returns all alerts matching the query.

func (*AlertView) GetID

func (a *AlertView) GetID() int64

func (*AlertView) HasBeenRead

func (a *AlertView) HasBeenRead() bool

func (*AlertView) Save

func (a *AlertView) Save() error

type AlertsPremisEvents

type AlertsPremisEvents struct {
	AlertID       int64
	PremisEventID int64
}

type AlertsUsers

type AlertsUsers struct {
	AlertID int64
	UserID  int64
	SentAt  time.Time
	ReadAt  time.Time
}

type AlertsWorkItems

type AlertsWorkItems struct {
	AlertID    int64
	WorkItemID int64
}

type BaseModel

type BaseModel struct {
	ID int64 `pg:"id" form:"id" json:"id"`
}

func (*BaseModel) GetID

func (bm *BaseModel) GetID() int64

func (*BaseModel) Save

func (bm *BaseModel) Save() error

type BillingStats

type BillingStats struct {
	InstitutionID   int64     `json:"institution_id"`
	InstitutionName string    `json:"institution_name"`
	EndDate         time.Time `json:"end_date"`
	MonthAndYear    string    `json:"month_and_year"`
	StorageOption   string    `json:"storage_option"`
	TotalGB         float64   `json:"total_gb"`
	TotalTB         float64   `json:"total_tb"`
	Overage         float64   `json:"overage"`
}

func BillingStatsSelect

func BillingStatsSelect(institutionID int64, startDate, endDate time.Time, storageOption string) ([]*BillingStats, error)

type Checksum

type Checksum struct {
	TimestampModel
	Algorithm     string       `json:"algorithm"`
	DateTime      time.Time    `json:"datetime" pg:"datetime"`
	Digest        string       `json:"digest"`
	GenericFileID int64        `json:"generic_file_id" pg:"generic_file_id"`
	GenericFile   *GenericFile `json:"-" pg:"rel:has-one"`
}

func ChecksumByID

func ChecksumByID(id int64) (*Checksum, error)

ChecksumByID returns the file with the specified id. Returns pg.ErrNoRows if there is no match.

func ChecksumGet

func ChecksumGet(query *Query) (*Checksum, error)

ChecksumGet returns the first file matching the query.

func ChecksumSelect

func ChecksumSelect(query *Query) ([]*Checksum, error)

ChecksumSelect returns all files matching the query.

func RandomChecksum

func RandomChecksum(alg string) *Checksum

RandomChecksum returns a random checksum with the specified algorithm. Caller should set GenericFileID.

func (*Checksum) Save

func (cs *Checksum) Save() error

Save saves this file to the database. This will peform an insert if Checksum.ID is zero. Otherwise, it returns an error, since updating checksums is not allowed. Checksums should only ever change on re-ingest, when we get a new version of an existing file. In that case, we add a new checksum, so that we have records for all checksums that have existed over time.

func (*Checksum) Validate

func (cs *Checksum) Validate() *common.ValidationError

type ChecksumView

type ChecksumView struct {
	ID                    int64     `json:"id"`
	Algorithm             string    `json:"algorithm"`
	DateTime              time.Time `json:"datetime" pg:"datetime"`
	Digest                string    `json:"digest"`
	State                 string    `json:"state"`
	GenericFileIdentifier string    `json:"generic_file_identifier"`
	GenericFileID         int64     `json:"generic_file_id"`
	IntellectualObjectID  int64     `json:"intellectual_object_id"`
	InstitutionID         int64     `json:"institution_id"`
	CreatedAt             time.Time `json:"created_at"`
	UpdatedAt             time.Time `json:"updated_at"`
	// contains filtered or unexported fields
}

func ChecksumViewByID

func ChecksumViewByID(id int64) (*ChecksumView, error)

ChecksumViewByID returns the file with the specified id. Returns pg.ErrNoRows if there is no match.

func ChecksumViewGet

func ChecksumViewGet(query *Query) (*ChecksumView, error)

ChecksumViewGet returns the first file matching the query.

func ChecksumViewSelect

func ChecksumViewSelect(query *Query) ([]*ChecksumView, error)

ChecksumViewSelect returns all files matching the query.

type DeletionRequest

type DeletionRequest struct {
	BaseModel
	InstitutionID              int64                 `json:"institution_id"`
	RequestedByID              int64                 `json:"-"`
	RequestedAt                time.Time             `json:"requested_at"`
	ConfirmationToken          string                `json:"-" pg:"-"`
	EncryptedConfirmationToken string                `json:"-"`
	ConfirmedByID              int64                 `json:"-"`
	ConfirmedAt                time.Time             `json:"confirmed_at"`
	CancelledByID              int64                 `json:"-"`
	CancelledAt                time.Time             `json:"cancelled_at"`
	RequestedBy                *User                 `json:"requested_by" pg:"rel:has-one"`
	ConfirmedBy                *User                 `json:"confirmed_by" pg:"rel:has-one"`
	CancelledBy                *User                 `json:"cancelled_by" pg:"rel:has-one"`
	WorkItemID                 int64                 `json:"work_item_id"`
	GenericFiles               []*GenericFile        `json:"generic_files" pg:"many2many:deletion_requests_generic_files"`
	IntellectualObjects        []*IntellectualObject `json:"intellectual_objects" pg:"many2many:deletion_requests_intellectual_objects"`
	WorkItem                   *WorkItem             `json:"work_item" pg:"rel:has-one"`
}

func CreateDeletionRequest

func CreateDeletionRequest(objects []*IntellectualObject, files []*GenericFile) (*DeletionRequest, error)

func DeletionRequestByID

func DeletionRequestByID(id int64) (*DeletionRequest, error)

DeletionRequestByID returns the institution with the specified id. Returns pg.ErrNoRows if there is no match.

func DeletionRequestGet

func DeletionRequestGet(query *Query) (*DeletionRequest, error)

DeletionRequestGet returns the first deletion request matching the query.

func DeletionRequestSelect

func DeletionRequestSelect(query *Query) ([]*DeletionRequest, error)

DeletionRequestSelect returns all deletion requests matching the query.

func NewDeletionRequest

func NewDeletionRequest() (*DeletionRequest, error)

func (*DeletionRequest) AddFile

func (request *DeletionRequest) AddFile(gf *GenericFile)

func (*DeletionRequest) AddObject

func (request *DeletionRequest) AddObject(obj *IntellectualObject)

func (*DeletionRequest) Cancel

func (request *DeletionRequest) Cancel(user *User)

Cancel cancels this DeletionRequest. It's up to the caller to save this request after cancelling it.

func (*DeletionRequest) Confirm

func (request *DeletionRequest) Confirm(user *User)

Confirm marks this DeletionRequest as confirmed. It's up to the caller to save the request and create an appropriate WorkItem.

func (*DeletionRequest) FirstFile

func (request *DeletionRequest) FirstFile() *GenericFile

FirstFile returns the first GenericFile associated with this deletion request. Use this for simple, single-file deletions.

func (*DeletionRequest) FirstObject

func (request *DeletionRequest) FirstObject() *IntellectualObject

FirstObject returns the first IntellectualObject associated with this deletion request. Use this for simple, single-object deletions.

func (*DeletionRequest) Save

func (request *DeletionRequest) Save() error

Save saves this requestitution to the database. This will peform an insert if DeletionRequest.ID is zero. Otherwise, it updates.

func (*DeletionRequest) ToMin

func (r *DeletionRequest) ToMin() *DeletionRequestMin

ToMin returns DeletionRequestMin object suitable for serialization in the member API.

func (*DeletionRequest) Validate

func (request *DeletionRequest) Validate() *common.ValidationError

Validation enforces business rules, including who can request and confirm deletions. Although our general security middleware should prevent any of these problems from ever occurring, we want to double check everything here because we're a preservation archive and deletion is a destructive action. We must be sure deletion is a deliberate act initiated and confirmed by authorized individuals.

type DeletionRequestMin

type DeletionRequestMin struct {
	ID                  int64                 `json:"id"`
	InstitutionID       int64                 `json:"institution_id"`
	RequestedAt         time.Time             `json:"requested_at"`
	ConfirmedAt         time.Time             `json:"confirmed_at"`
	CancelledAt         time.Time             `json:"cancelled_at"`
	RequestedBy         *UserMin              `json:"requested_by"`
	ConfirmedBy         *UserMin              `json:"confirmed_by"`
	CancelledBy         *UserMin              `json:"cancelled_by"`
	GenericFiles        []*GenericFile        `json:"generic_files"`
	IntellectualObjects []*IntellectualObject `json:"intellectual_objects"`
	WorkItem            *WorkItem             `json:"work_item"`
}

DeletionRequestMin includes the subset of DeletionRequest info that we want to expose through the member API.

type DeletionRequestView

type DeletionRequestView struct {
	ID                    int64     `json:"id"`
	InstitutionID         int64     `json:"institution_id"`
	InstitutionName       string    `json:"institution_name"`
	InstitutionIdentifier string    `json:"institution_identifier"`
	RequestedByID         int64     `json:"requested_by_id"`
	RequestedByName       string    `json:"requested_by_name"`
	RequestedByEmail      string    `json:"requested_by_email"`
	RequestedAt           time.Time `json:"requested_at"`
	ConfirmedByID         int64     `json:"confirmed_by_id"`
	ConfirmedByName       string    `json:"confirmed_by_name"`
	ConfirmedByEmail      string    `json:"confirmed_by_email"`
	ConfirmedAt           time.Time `json:"confirmed_at"`
	CancelledByID         int64     `json:"cancelled_by_id"`
	CancelledByName       string    `json:"cancelled_by_name"`
	CancelledByEmail      string    `json:"cancelled_by_email"`
	CancelledAt           time.Time `json:"cancelled_at"`
	FileCount             int64     `json:"file_count"`
	ObjectCount           int64     `json:"object_count"`
	WorkItemID            int64     `json:"work_item_id"`
	Stage                 string    `json:"stage"`
	Status                string    `json:"status"`
	DateProcessed         time.Time `json:"date_processed"`
	Size                  int64     `json:"size"`
	Note                  string    `json:"note"`
	// contains filtered or unexported fields
}

DeletionRequestsView contains a flattened view of deletion requests suitable for the index page.

func DeletionRequestViewByID

func DeletionRequestViewByID(id int64) (*DeletionRequestView, error)

DeletionRequestViewByID returns the DeletionRequestView record with the specified id. Returns pg.ErrNoRows if there is no match.

func DeletionRequestViewGet

func DeletionRequestViewGet(query *Query) (*DeletionRequestView, error)

DeletionRequestViewGet returns the first user view record matching the query.

func DeletionRequestViewSelect

func DeletionRequestViewSelect(query *Query) ([]*DeletionRequestView, error)

DeletionRequestViewSelect returns all DeletionRequestView records matching the query.

func (*DeletionRequestView) DisplayStatus

func (request *DeletionRequestView) DisplayStatus() string

DisplayStatus returns a string saying whether this deletion request has been cancelled, is in progress, or complete, or whatever.

type DeletionRequestsGenericFiles

type DeletionRequestsGenericFiles struct {
	DeletionRequestID int64
	GenericFileID     int64
	// contains filtered or unexported fields
}

type DeletionRequestsIntellectualObjects

type DeletionRequestsIntellectualObjects struct {
	DeletionRequestID    int64
	IntellectualObjectID int64
	// contains filtered or unexported fields
}

type DepositFormatStats

type DepositFormatStats struct {
	FileFormat string  `json:"file_format"`
	FileCount  int64   `json:"file_count"`
	TotalBytes int64   `json:"total_bytes"`
	TotalGB    float64 `json:"total_gb" pg:"total_gb"`
	TotalTB    float64 `json:"total_tb" pg:"total_tb"`
}

func DepositFormatStatsSelect

func DepositFormatStatsSelect(institutionID, intellectualObjectID int64) ([]*DepositFormatStats, error)

DepositFormatStatsSelect returns summary stats on the files belonging to the specified institution and/or object. Specify object ID for object status, Institution ID for institution status.

Note that stats come back in different order on MacOs vs Linux. https://dba.stackexchange.com/questions/106964/why-is-my-postgresql-order-by-case-insensitive

func StatsByFormat

func StatsByFormat(stats []*DepositFormatStats, format string) *DepositFormatStats

StatsByFormat returns the stats for the specified format, or nil if not found.

type DepositStats

type DepositStats struct {
	InstitutionID       int64     `json:"institution_id"`
	MemberInstitutionID int64     `json:"member_institution_id"`
	InstitutionName     string    `json:"institution_name"`
	StorageOption       string    `json:"storage_option"`
	ObjectCount         int64     `json:"object_count"`
	FileCount           int64     `json:"file_count"`
	TotalBytes          int64     `json:"total_bytes"`
	TotalGB             float64   `json:"total_gb" pg:"total_gb"`
	TotalTB             float64   `json:"total_tb" pg:"total_tb"`
	CostGBPerMonth      float64   `json:"cost_gb_per_month" pg:"cost_gb_per_month"`
	MonthlyCost         float64   `json:"monthly_cost"`
	EndDate             time.Time `json:"end_date"`
	PrimarySort         string    `json:"-"`
	SecondarySort       string    `json:"-"`
}

DepositStats contains info about member deposits and the costs of those deposits. This struct does not implement the usual pgmodel interface, nor does it map to a single underlying table or view. This struct merely represents to the output of a reporting query.

Note that we store CostGBPerMonth and MonthlyCost in the table rather than calculating them because cost per GB per month may change over time, and we want to capture the actual historical cost for each month.

func DepositStatsOverTime

func DepositStatsOverTime(institutionID int64, storageOption string, startDate, endDate time.Time) ([]*DepositStats, error)

func DepositStatsSelect

func DepositStatsSelect(institutionID int64, storageOption string, endDate time.Time) ([]*DepositStats, error)

DepositStatsSelect returns info about materials a depositor updated in our system before a given date. This breaks down deposits by storage option and institution. To report on all institutions, use zero for institutionID. To report on all storage options, pass an empty string for storageOption.

type FilterCollection

type FilterCollection struct {
	// contains filtered or unexported fields
}

FilterCollection converts query string params such as name__eq=Homer to a pgmodels.Query object that allows us to build a SQL where clause as we go.

func NewFilterCollection

func NewFilterCollection() *FilterCollection

NewFilterCollection returns a ParamFileters object.

func (*FilterCollection) Add

func (fc *FilterCollection) Add(key string, values []string) (*ParamFilter, error)

Add adds an item to the filter collection. Param key is a filter key from the query string. Param values are the values associated with that key. For example:

Key: name__in Values: ["Bart", "Lisa", "Maggie"]

func (*FilterCollection) AddOrderBy

func (fc *FilterCollection) AddOrderBy(colAndDir string)

AddOrderBy adds sort columns to the filters. Param colAndDir should be in format "column_name__dir", where dir is "asc" or "desc". If dir is omitted, it defaults to "asc."

Examples: "created_at__desc", "id__asc".

func (*FilterCollection) HasExplicitSorting

func (fc *FilterCollection) HasExplicitSorting() bool

HasExplicitSorting returns true if this object includes explicit sort params that will show up as an "order by" clause in the SQL query.

func (*FilterCollection) ToQuery

func (fc *FilterCollection) ToQuery() (*Query, error)

ToQuery returns a query object based on the keys and values passed in. The Query's WhereClause() will return the where conditions for the filters passed in through Add(), and the Query's Params() method will return the params. Conditions and params come back in the order they were added.

func (*FilterCollection) ValueOf

func (fc *FilterCollection) ValueOf(filterName string) string

ValueOf returns the value of the filter with the specified name. Returns an empty string if the specified filter is missing or has no value.

func (*FilterCollection) ValuesOf

func (fc *FilterCollection) ValuesOf(filterName string) []string

ValuesOf returns all selected values for the specified filter.

type GenericFile

type GenericFile struct {
	TimestampModel
	FileFormat           string              `json:"file_format"`
	Size                 int64               `json:"size"`
	Identifier           string              `json:"identifier"`
	IntellectualObjectID int64               `json:"intellectual_object_id"`
	State                string              `json:"state"`
	LastFixityCheck      time.Time           `json:"last_fixity_check"`
	InstitutionID        int64               `json:"institution_id"`
	StorageOption        string              `json:"storage_option"`
	UUID                 string              `json:"uuid" pg:"uuid"`
	Institution          *Institution        `json:"-" pg:"rel:has-one"`
	IntellectualObject   *IntellectualObject `json:"-" pg:"rel:has-one"`
	PremisEvents         []*PremisEvent      `json:"premis_events" pg:"rel:has-many"`
	Checksums            []*Checksum         `json:"checksums" pg:"rel:has-many"`
	StorageRecords       []*StorageRecord    `json:"storage_records" pg:"rel:has-many"`
}

func GenericFileByID

func GenericFileByID(id int64) (*GenericFile, error)

GenericFileByID returns the file with the specified id. Returns pg.ErrNoRows if there is no match.

func GenericFileByIdentifier

func GenericFileByIdentifier(identifier string) (*GenericFile, error)

GenericFileByIdentifier returns the file with the specified identifier. Returns pg.ErrNoRows if there is no match.

func GenericFileGet

func GenericFileGet(query *Query) (*GenericFile, error)

GenericFileGet returns the first file matching the query.

func GenericFileSelect

func GenericFileSelect(query *Query) ([]*GenericFile, error)

GenericFileSelect returns all files matching the query. Note that this returns related objects as well, including PremisEvents, Checksums, and StorageRecords.

func ObjectFiles

func ObjectFiles(objID int64, filter, state string, offset, limit int) ([]*GenericFile, error)

Object files returns files belonging to an intellectual object. This function is used to filter files on the IntellectualObjectShow page. objID is the id of the object. filter is an optional file identifier or checksum. offset and limit are for paging. state is "A" for active (default) or "D" for deleted.

func RandomGenericFile

func RandomGenericFile(objID int64, objIdentifier string) *GenericFile

RandomGenericFile returns a random generic file with the specified obj identifier prefix. State will be active.

func (*GenericFile) ActiveDeletionWorkItem

func (gf *GenericFile) ActiveDeletionWorkItem() (*WorkItem, error)

ActiveDeletionWorkItem returns the in-progress WorkItem for this file's deletion. The WorkItem may be for the deletion of this specific file, or of its parent object.

func (*GenericFile) AssertDeletionPreconditions

func (gf *GenericFile) AssertDeletionPreconditions() error

func (*GenericFile) Delete

func (gf *GenericFile) Delete() error

Delete soft-deletes this file by setting State to 'D' and the UpdatedAt timestamp to now. You can undo this with Undelete. It also creates a deletion PremisEvent. You can't get rid of that.

It is legitimate for a depositor to delete a file, then re-upload it later, particularly if they want to change the storage option. In that case, the file's state would be set back to "A" after the new ingest, and the old deletion event would remain to show that an earlier version of the file was once deleted.

We would know the new file is active because state = "A" and it would have an ingest event dated after the last deletion event.

func (*GenericFile) DeletionRequest

func (gf *GenericFile) DeletionRequest(workItemID int64) (*DeletionRequestView, error)

func (*GenericFile) IsGlacierOnly

func (gf *GenericFile) IsGlacierOnly() bool

IsGlacierOnly returns true if this file is stored only in Glacier.

func (*GenericFile) LastDeletionEvent

func (gf *GenericFile) LastDeletionEvent() (*PremisEvent, error)

LastDeleationEvent returns the latest deletion event for this file, which may be nil.

func (*GenericFile) LastIngestEvent

func (gf *GenericFile) LastIngestEvent() (*PremisEvent, error)

LastIngestEvent returns the latest ingest event for this file. This should never be nil.

func (*GenericFile) NewDeletionEvent

func (gf *GenericFile) NewDeletionEvent() (*PremisEvent, error)

func (*GenericFile) Save

func (gf *GenericFile) Save() error

Save saves this file to the database. This will peform an insert if GenericFile.ID is zero. Otherwise, it updates.

Note that the insert/update also saves all associated records (checksums, storage records, and premis events).

func (*GenericFile) Validate

func (gf *GenericFile) Validate() *common.ValidationError

func (*GenericFile) ValidateChanges

func (gf *GenericFile) ValidateChanges(updatedFile *GenericFile) error

type GenericFileCount

type GenericFileCount struct {
	InstitutionID int64  `json:"institution_id"`
	RowCount      int    `json:"row_count"`
	State         string `json:"state"`
	// contains filtered or unexported fields
}

type GenericFileView

type GenericFileView struct {
	ID                    int64     `json:"id" pg:"id"`
	FileFormat            string    `json:"file_format"`
	Size                  int64     `json:"size"`
	Identifier            string    `json:"identifier"`
	IntellectualObjectID  int64     `json:"intellectual_object_id"`
	ObjectIdentifier      string    `json:"object_identifier"`
	Access                string    `json:"access"`
	State                 string    `json:"state"`
	LastFixityCheck       time.Time `json:"last_fixity_check"`
	InstitutionID         int64     `json:"institution_id"`
	InstitutionName       string    `json:"institution_name"`
	InstitutionIdentifier string    `json:"institution_identifier"`
	StorageOption         string    `json:"storage_option"`
	UUID                  string    `json:"uuid" pg:"uuid"`
	Md5                   string    `json:"md5"`
	Sha1                  string    `json:"sha1"`
	Sha256                string    `json:"sha256"`
	Sha512                string    `json:"sha512"`
	CreatedAt             time.Time `json:"created_at"`
	UpdatedAt             time.Time `json:"updated_at"`

	// This is a late hack. Needed for object restoration.
	// Note that there's no join the DB for this (and there shouldn't
	// be, because it would produce multiple records). The
	// GenericFilesController will hack in the storage records
	// only when specifically requested.
	StorageRecords []*StorageRecord `pg:"-" json:"storage_records,omitempty"`
	// contains filtered or unexported fields
}

func GenericFileViewByID

func GenericFileViewByID(id int64) (*GenericFileView, error)

GenericFileViewByID returns the GenericFileView record with the specified id. Returns pg.ErrNoRows if there is no match.

func GenericFileViewByIdentifier

func GenericFileViewByIdentifier(identifier string) (*GenericFileView, error)

GenericFileViewByIdentifier returns the GenericFileView record with the specified email address. Returns pg.ErrNoRows if there is no match.

func GenericFileViewGet

func GenericFileViewGet(query *Query) (*GenericFileView, error)

GenericFileViewGet returns the first user view record matching the query.

func GenericFileViewSelect

func GenericFileViewSelect(query *Query) ([]*GenericFileView, error)

GenericFileViewSelect returns all GenericFileView records matching the query.

type Institution

type Institution struct {
	TimestampModel
	Name                      string    `json:"name"`
	Identifier                string    `json:"identifier"`
	State                     string    `json:"state"`
	Type                      string    `json:"type"`
	MemberInstitutionID       int64     `json:"member_institution_id"`
	DeactivatedAt             time.Time `json:"deactivated_at"`
	OTPEnabled                bool      `json:"otp_enabled" pg:",use_zero"`
	SpotRestoreFrequency      int64     `json:"spot_restore_frequency" pg:",use_zero"`
	LastSpotRestoreWorkItemID int64     `json:"last_spot_restore_work_item_id"`
	ReceivingBucket           string    `json:"receiving_bucket"`
	RestoreBucket             string    `json:"restore_bucket"`
}

func InstitutionByID

func InstitutionByID(id int64) (*Institution, error)

InstitutionByID returns the institution with the specified id. Returns pg.ErrNoRows if there is no match.

func InstitutionByIdentifier

func InstitutionByIdentifier(identifier string) (*Institution, error)

InstitutionByIdentifier returns the institution with the specified identifier. Returns pg.ErrNoRows if there is no match.

func InstitutionGet

func InstitutionGet(query *Query) (*Institution, error)

InstitutionGet returns the first institution matching the query.

func InstitutionSelect

func InstitutionSelect(query *Query) ([]*Institution, error)

InstitutionSelect returns all institutions matching the query.

func (*Institution) Delete

func (inst *Institution) Delete() error

Delete soft-deletes this institution by setting State to 'D' and the DeletedAt timestamp to now. You can undo this with Undelete.

func (*Institution) DisplayType

func (i *Institution) DisplayType() string

DisplayType returns either "Member" or "Associate" depending on the institution type. This is the type as we display it to users, not as we store it in the database. This method exists because we changed terminology in 2023.

"Subscription Institution", "Sub-Account", "Associate Member" and "Associate" all mean the same thing, but we're using "Associate" in the UI.

func (*Institution) DueForSpotRestore

func (inst *Institution) DueForSpotRestore() (bool, error)

DueForSpotRestore returns true if this institution is due for a restoration spot test.

func (*Institution) GetAssociateMembers

func (inst *Institution) GetAssociateMembers() ([]*Institution, error)

GetAssociateMembers returns a list of the associate members (aka sub-accounts) belonging to this subscribing member. This will return an empty list if the institution itself is an associate account, or if it has zero subscribing members.

func (*Institution) HasSubAccounts

func (inst *Institution) HasSubAccounts() (bool, error)

HasSubAccounts returns true if this insitution has active sub-accounts, aka associate members.

func (*Institution) Save

func (inst *Institution) Save() error

Save saves this institution to the database. This will peform an insert if Institution.ID is zero. Otherwise, it updates.

func (*Institution) Undelete

func (inst *Institution) Undelete() error

Undelete reactivates this institution by setting State to 'A' and clearing the DeletedAt timestamp.

func (*Institution) Validate

func (inst *Institution) Validate() *common.ValidationError

Validate validates the model. This is called automatically on insert and update.

type InstitutionView

type InstitutionView struct {
	ID                        int64     `json:"id"`
	Name                      string    `json:"name"`
	Identifier                string    `json:"identifier"`
	State                     string    `json:"state"`
	Type                      string    `json:"type"`
	DeactivatedAt             time.Time `json:"deactivated_at"`
	OTPEnabled                bool      `json:"otp_enabled"`
	SpotRestoreFrequency      int64     `json:"spot_restore_frequency" pg:",use_zero"`
	LastSpotRestoreWorkItemID int64     `json:"last_spot_restore_work_item_id"`
	ReceivingBucket           string    `json:"receiving_bucket"`
	RestoreBucket             string    `json:"restore_bucket"`
	CreatedAt                 time.Time `json:"created_at"`
	UpdatedAt                 time.Time `json:"updated_at"`
	ParentId                  int64     `json:"parent_id"`
	ParentName                string    `json:"parent_name"`
	ParentIdentifier          string    `json:"parent_identifier"`
	ParentState               string    `json:"parent_state"`
	ParentDeactivatedAt       time.Time `json:"parent_deactivated_at"`
	// contains filtered or unexported fields
}

InstitutionView contains information about an institution and its parent (if it has one). This view simplifies both search and display for institution management.

func InstitutionViewByID

func InstitutionViewByID(id int64) (*InstitutionView, error)

InstitutionViewByID returns the InstitutionView record with the specified id. Returns pg.ErrNoRows if there is no match.

func InstitutionViewByIdentifier

func InstitutionViewByIdentifier(identifier string) (*InstitutionView, error)

InstitutionViewByIdentifier returns the InstitutionView record with the specified identifier (domain name). Returns pg.ErrNoRows if there is no match.

func InstitutionViewGet

func InstitutionViewGet(query *Query) (*InstitutionView, error)

InstitutionViewGet returns the first user view record matching the query.

func InstitutionViewSelect

func InstitutionViewSelect(query *Query) ([]*InstitutionView, error)

InstitutionViewSelect returns all InstitutionView records matching the query.

func (*InstitutionView) DisplayType

func (i *InstitutionView) DisplayType() string

DisplayType returns either "Member" or "Associate" depending on the institution type. This is the type as we display it to users, not as we store it in the database. This method exists because we changed terminology in 2023.

"Subscription Institution", "Sub-Account", "Associate Member" and "Associate" all mean the same thing, but we're using "Associate" in the UI.

type IntellectualObject

type IntellectualObject struct {
	TimestampModel
	Title                     string         `json:"title"`
	Description               string         `json:"description"`
	Identifier                string         `json:"identifier"`
	AltIdentifier             string         `json:"alt_identifier"`
	Access                    string         `json:"access"`
	BagName                   string         `json:"bag_name"`
	InstitutionID             int64          `json:"institution_id"`
	State                     string         `json:"state"`
	ETag                      string         `json:"etag" pg:"etag"`
	BagGroupIdentifier        string         `json:"bag_group_identifier"`
	StorageOption             string         `json:"storage_option"`
	BagItProfileIdentifier    string         `json:"bagit_profile_identifier" pg:"bagit_profile_identifier"`
	SourceOrganization        string         `json:"source_organization"`
	InternalSenderIdentifier  string         `json:"internal_sender_identifier"`
	InternalSenderDescription string         `json:"internal_sender_description"`
	Institution               *Institution   `json:"institution" pg:"rel:has-one"`
	GenericFiles              []*GenericFile `json:"generic_files" pg:"rel:has-many"`
	PremisEvents              []*PremisEvent `json:"premis_events" pg:"rel:has-many"`
}

func CreateObjectWithRelations

func CreateObjectWithRelations() (*IntellectualObject, error)

func GetTestObject

func GetTestObject() *IntellectualObject

GetTestObject returns an IntellectualObject with valid settings that can be altered per-test.

func IntellectualObjectByID

func IntellectualObjectByID(id int64) (*IntellectualObject, error)

IntellectualObjectByID returns the object with the specified id. Returns pg.ErrNoRows if there is no match.

func IntellectualObjectByIdentifier

func IntellectualObjectByIdentifier(identifier string) (*IntellectualObject, error)

IntellectualObjectByIdentifier returns the object with the specified identifier. Returns pg.ErrNoRows if there is no match.

func IntellectualObjectGet

func IntellectualObjectGet(query *Query) (*IntellectualObject, error)

IntellectualObjectGet returns the first object matching the query.

func IntellectualObjectSelect

func IntellectualObjectSelect(query *Query) ([]*IntellectualObject, error)

IntellectualObjectSelect returns all objects matching the query.

func RandomObject

func RandomObject() *IntellectualObject

func (*IntellectualObject) ActiveDeletionWorkItem

func (obj *IntellectualObject) ActiveDeletionWorkItem() (*WorkItem, error)

func (*IntellectualObject) AssertDeletionPreconditions

func (obj *IntellectualObject) AssertDeletionPreconditions() error

func (*IntellectualObject) Delete

func (obj *IntellectualObject) Delete() error

Delete soft-deletes this object by setting State to 'D' and the UpdatedAt timestamp to now. You can undo this with Undelete. It also creates a deletion PremisEvent. You can't get rid of that.

It is legitimate for a depositor to delete an object, then re-upload it later, particularly if they want to change the storage option. In that case, the object's state would be set back to "A" after the new ingest, and the old deletion event would remain to show that an earlier version of the object was once deleted.

We would know the new object is active because state = "A" and it would have an ingest event dated after the last deletion event.

func (*IntellectualObject) DeletionRequest

func (obj *IntellectualObject) DeletionRequest(workItemID int64) (*DeletionRequestView, error)

func (*IntellectualObject) HasActiveFiles

func (obj *IntellectualObject) HasActiveFiles() (bool, error)

HasActiveFiles returns true if this object has any active (non-deleted) files. We need to check this before marking an object as deleted. Do not mark deleted until all files have been marked deleted.

func (*IntellectualObject) IsGlacierOnly

func (obj *IntellectualObject) IsGlacierOnly() bool

IsGlacierOnly returns true if this object is stored only in Glacier.

func (*IntellectualObject) LastDeletionEvent

func (obj *IntellectualObject) LastDeletionEvent() (*PremisEvent, error)

LastDeleationEvent returns the latest deletion event for this object, which may be nil.

func (*IntellectualObject) LastIngestEvent

func (obj *IntellectualObject) LastIngestEvent() (*PremisEvent, error)

LastIngestEvent returns the latest ingest event for this object. This should never be nil.

func (*IntellectualObject) NewDeletionEvent

func (obj *IntellectualObject) NewDeletionEvent() (*PremisEvent, error)

func (*IntellectualObject) Save

func (obj *IntellectualObject) Save() error

Save saves this object to the database. This will peform an insert if IntellectualObject.ID is zero. Otherwise, it updates.

func (*IntellectualObject) Validate

func (obj *IntellectualObject) Validate() *common.ValidationError

func (*IntellectualObject) ValidateChanges

func (obj *IntellectualObject) ValidateChanges(updatedObj *IntellectualObject) error

type IntellectualObjectCount

type IntellectualObjectCount struct {
	InstitutionID int64  `json:"institution_id"`
	RowCount      int    `json:"row_count"`
	State         string `json:"state"`
	// contains filtered or unexported fields
}

type IntellectualObjectView

type IntellectualObjectView struct {
	ID                        int64     `json:"id"`
	Title                     string    `json:"title"`
	Description               string    `json:"description"`
	Identifier                string    `json:"identifier"`
	AltIdentifier             string    `json:"alt_identifier"`
	Access                    string    `json:"access"`
	BagName                   string    `json:"bag_name"`
	InstitutionID             int64     `json:"institution_id"`
	CreatedAt                 time.Time `json:"created_at"`
	UpdatedAt                 time.Time `json:"updated_at"`
	State                     string    `json:"state"`
	ETag                      string    `json:"etag" pg:"etag"`
	BagGroupIdentifier        string    `json:"bag_group_identifier"`
	StorageOption             string    `json:"storage_option"`
	BagItProfileIdentifier    string    `json:"bagit_profile_identifier" pg:"bagit_profile_identifier"`
	SourceOrganization        string    `json:"source_organization"`
	InternalSenderIdentifier  string    `json:"internal_sender_identifier"`
	InternalSenderDescription string    `json:"internal_sender_description"`
	InstitutionName           string    `json:"institution_name"`
	InstitutionIdentifier     string    `json:"institution_identifier"`
	InstitutionType           string    `json:"institution_type"`
	InstitutionParentID       int64     `json:"institution_parent_id"`
	FileCount                 int64     `json:"file_count"`
	Size                      int64     `json:"size"`
	PayloadFileCount          int64     `json:"payload_file_count"`
	PayloadSize               int64     `json:"payload_size"`
	// contains filtered or unexported fields
}

func IntellectualObjectViewByID

func IntellectualObjectViewByID(id int64) (*IntellectualObjectView, error)

IntellectualObjectViewByID returns the object with the specified id. Returns pg.ErrNoRows if there is no match.

func IntellectualObjectViewByIdentifier

func IntellectualObjectViewByIdentifier(identifier string) (*IntellectualObjectView, error)

IntellectualObjectViewByIdentifier returns the object with the specified identifier. Returns pg.ErrNoRows if there is no match.

func IntellectualObjectViewGet

func IntellectualObjectViewGet(query *Query) (*IntellectualObjectView, error)

IntellectualObjectViewGet returns the first object matching the query.

func IntellectualObjectViewSelect

func IntellectualObjectViewSelect(query *Query) ([]*IntellectualObjectView, error)

IntellectualObjectViewSelect returns all objects matching the query.

func SmallestObjectNotRestoredInXDays

func SmallestObjectNotRestoredInXDays(institutionID, minSize int64, days int) (*IntellectualObjectView, error)

SmallestObjectNotRestoredInXDays returns the smallest intellectual object belonging to an institutition that has not been restored in at least X days. We use this for restoration spot tests because 1) we don't want to restore an item the depositor has recently restored, and 2) we don't want to restore massive objects (hundreds of GB) if we can help it.

Param institutionID is the ID of the depositing institution. minSize is the minimum size of the object to restore. This should generally be around 1-20 KB. days is the number of days since last restoration. This should be 365 or more for spot tests, perferably 730 or more.

func (*IntellectualObjectView) GetID

func (obj *IntellectualObjectView) GetID() int64

type InternalMetadata

type InternalMetadata struct {
	TimestampModel
	Key   string
	Value string
	// contains filtered or unexported fields
}

InternalMetadata contains metadata about the state of the database. This is a holdover from Rails and ActiveRecord which turns out to be useful for housekeeping.

We primarily use it as a kind of locking mechanism to ensure that the database's internal processes don't overlap. Internal processes set a key and value when they're running, then clear the value when they're done.

For example, you'll see pairs like "update_counts is running" = true when the long-running update_counts function is doing its work. This prevents a second process from starting update_counts before the last process has completed, thus avoiding deadlock.

Registry uses this table when running restoration spot tests, which should run once per day. While we may have two or more Registry instances running at once, we want to make sure restoration spot tests run **only once** per day. This table helps with that.

func InternalMetadataByKey

func InternalMetadataByKey(key string) (*InternalMetadata, error)

InternalMetadataByKey returns the file with the specified key. The key column has a unique constraint, so this should return one record, max. Returns pg.ErrNoRows if there is no match.

func InternalMetadataGet

func InternalMetadataGet(query *Query) (*InternalMetadata, error)

InternalMetadataGet returns the first file matching the query.

func InternalMetadataSelect

func InternalMetadataSelect(query *Query) ([]*InternalMetadata, error)

InternalMetadataSelect returns all files matching the query.

func NewInteralMetadata

func NewInteralMetadata(key, value string) *InternalMetadata

NewInternalMetadata creates a new InternalMetadata record.

func (*InternalMetadata) Delete

func (im *InternalMetadata) Delete() error

Delete is not supported because these records are required for internal housekeeping. This method will always return an error. We define to tell developers explicitly not to do this.

func (*InternalMetadata) Save

func (im *InternalMetadata) Save() error

Save saves this record to the database. This will peform an insert if InternalMetadata.ID is zero or an update otherwise.

func (*InternalMetadata) Validate

func (im *InternalMetadata) Validate() *common.ValidationError

type Model

type Model interface {
	GetID() int64
	Save() error
	Validate() *common.ValidationError
}

type ParamFilter

type ParamFilter struct {
	// Key is the name of the query string param.
	Key string
	// Column is derived from Key, and is the name of a database column
	Column string
	// RawOp is the operator in Key. For example, "eq" or "gt".
	RawOp string
	// SQLOp is the SQL operator that corresponds to RawOp. For example,
	// "=" or ">".
	SQLOp string
	// Values are the values attached to Key in the query string.
	Values []string
}

ParamFilter parses query string params into filters that can be added to a SQL where clause.

func NewParamFilter

func NewParamFilter(key string, values []string) (*ParamFilter, error)

NewParamFilter returns a new ParamFilter object based on the key and values submitted in the query string. It will return a custom error if it can't parse the key or values. The caller should log the error and return a basic common.ErrInvalidParam.

func (*ParamFilter) AddToQuery

func (pf *ParamFilter) AddToQuery(q *Query) error

AddToQuery adds this ParamFilter to SQL query q. If it can't map the RawOp to a known Query method, it returns a custom error. The caller should log the error and then return a basic common.ErrInvalidParam.

func (*ParamFilter) ChipLabel

func (p *ParamFilter) ChipLabel() string

ChipLabel returns a label to display on filter chips in the web UI.

func (*ParamFilter) ChipValue

func (p *ParamFilter) ChipValue() string

ChipValue returns a value to display on filter chips in the web UI.

func (*ParamFilter) InterfaceValues

func (pf *ParamFilter) InterfaceValues() []interface{}

InterfaceValues converts []string Values to []interface{} values. We get string values from the HTTP query string, but we need to provide interface{} values to the pg library that will query the database. Golang type suckage.

type PremisEvent

type PremisEvent struct {
	TimestampModel
	Agent                string    `json:"agent"`
	DateTime             time.Time `json:"date_time"`
	Detail               string    `json:"detail"`
	EventType            string    `json:"event_type"`
	GenericFileID        int64     `json:"generic_file_id"`
	Identifier           string    `json:"identifier"`
	InstitutionID        int64     `json:"institution_id"`
	IntellectualObjectID int64     `json:"intellectual_object_id"`
	Object               string    `json:"object"`
	OldUUID              string    `json:"old_uuid"`
	Outcome              string    `json:"outcome"`
	OutcomeDetail        string    `json:"outcome_detail"`
	OutcomeInformation   string    `json:"outcome_information"`
}

func PremisEventByID

func PremisEventByID(id int64) (*PremisEvent, error)

PremisEventByID returns the event with the specified id. Returns pg.ErrNoRows if there is no match.

func PremisEventByIdentifier

func PremisEventByIdentifier(identifier string) (*PremisEvent, error)

PremisEventByIdentifier returns the event with the specified identifier. Returns pg.ErrNoRows if there is no match.

func PremisEventGet

func PremisEventGet(query *Query) (*PremisEvent, error)

PremisEventGet returns the first event matching the query.

func PremisEventSelect

func PremisEventSelect(query *Query) ([]*PremisEvent, error)

PremisEventSelect returns all events matching the query.

func RandomPremisEvent

func RandomPremisEvent(eventType string) *PremisEvent

RandomPremisEvent returns a random premis event of the specified type. Caller should set GenericFileID and IntellectualObjectID.

func (*PremisEvent) Save

func (event *PremisEvent) Save() error

Save saves this event to the database. This will peform an insert if PremisEvent.ID is zero. Otherwise, it updates.

func (*PremisEvent) Validate

func (event *PremisEvent) Validate() *common.ValidationError

Validate returns errors if this event isn't valid.

type PremisEventCount

type PremisEventCount struct {
	InstitutionID int64  `json:"institution_id"`
	RowCount      int    `json:"row_count"`
	EventType     string `json:"event_type"`
	Outcome       string `json:"outcome"`
	// contains filtered or unexported fields
}

type PremisEventView

type PremisEventView struct {
	ID                           int64     `json:"id" form:"id"`
	Agent                        string    `json:"agent"`
	CreatedAt                    time.Time `json:"created_at"`
	DateTime                     time.Time `json:"date_time"`
	Detail                       string    `json:"detail"`
	EventType                    string    `json:"event_type"`
	GenericFileID                int64     `json:"generic_file_id"`
	GenericFileIdentifier        string    `json:"generic_file_identifier"`
	Identifier                   string    `json:"identifier"`
	InstitutionID                int64     `json:"institution_id"`
	InstitutionName              string    `json:"institution_name"`
	IntellectualObjectID         int64     `json:"intellectual_object_id"`
	IntellectualObjectIdentifier string    `json:"intellectual_object_identifier"`
	Object                       string    `json:"object"`
	OldUUID                      string    `json:"old_uuid"`
	Outcome                      string    `json:"outcome"`
	OutcomeDetail                string    `json:"outcome_detail"`
	OutcomeInformation           string    `json:"outcome_information"`
	UpdatedAt                    time.Time `json:"updated_at"`
	// contains filtered or unexported fields
}

func PremisEventViewByID

func PremisEventViewByID(id int64) (*PremisEventView, error)

PremisEventViewByID returns the event with the specified id. Returns pg.ErrNoRows if there is no match.

func PremisEventViewByIdentifier

func PremisEventViewByIdentifier(identifier string) (*PremisEventView, error)

PremisEventViewByIdentifier returns the event with the specified identifier. Returns pg.ErrNoRows if there is no match.

func PremisEventViewGet

func PremisEventViewGet(query *Query) (*PremisEventView, error)

PremisEventViewGet returns the first event matching the query.

func PremisEventViewSelect

func PremisEventViewSelect(query *Query) ([]*PremisEventView, error)

PremisEventViewSelect returns all events matching the query.

type Query

type Query struct {
	// contains filtered or unexported fields
}

Query provides a fluent model for constructing SQL queries, though it currently constructs only the where, order by, limit and offset clauses, which is all we need to pass to the go-pg adapter. The adapter knows how to construct the select and from clauses based on the model.

Query's functionality is a subset of go-pg's query builder features. It doesn't support joins, for example, though it does support relations.

Query exists to help the web front-end construct queries based on named inputs in GET requests. The web.FilterCollection functions can dynamically translate query string params into complex where clauses like "name ilike '%homer%' and age >= 31 and city not in ('Shelbyville', 'Atlanta', 'New York')".

We decided to omit complex joins in our dynamic queries and create SQL views instead, because after several years in production, we already know virtually all of the ways users want to join tables. Views allow us to issue simple queries, which this package supports.

func NewQuery

func NewQuery() *Query

func (*Query) And

func (q *Query) And(cols, ops []string, vals []interface{}) *Query

func (*Query) BetweenExclusive

func (q *Query) BetweenExclusive(col string, low, high interface{}) *Query

func (*Query) BetweenInclusive

func (q *Query) BetweenInclusive(col string, low, high interface{}) *Query

func (*Query) Columns

func (q *Query) Columns(cols ...string) *Query

func (*Query) CopyForCount

func (q *Query) CopyForCount() *Query

CopyForCount returns a copy of this query suitable for querying one of our counts views. See pgmodels/counts.go for usage.

func (*Query) Count

func (q *Query) Count(model interface{}) (int, error)

func (*Query) GetColumns

func (q *Query) GetColumns() []string

func (*Query) GetColumnsInWhereClause

func (q *Query) GetColumnsInWhereClause() []string

func (*Query) GetLimit

func (q *Query) GetLimit() int

func (*Query) GetOffset

func (q *Query) GetOffset() int

func (*Query) GetOrderBy

func (q *Query) GetOrderBy() []string

func (*Query) GetRelations

func (q *Query) GetRelations() []string

func (*Query) IsNotNull

func (q *Query) IsNotNull(col string) *Query

func (*Query) IsNull

func (q *Query) IsNull(col string) *Query

func (*Query) Limit

func (q *Query) Limit(limit int) *Query

func (*Query) MakePlaceholders

func (q *Query) MakePlaceholders(start, count int) string

func (*Query) Offset

func (q *Query) Offset(offset int) *Query

func (*Query) Or

func (q *Query) Or(cols, ops []string, vals []interface{}) *Query

func (*Query) OrderBy

func (q *Query) OrderBy(column, direction string) *Query

OrderBy adds an "order by" clause to the query. You can add as many of these as you want. If direction is not "desc" it will be coerced to "asc". Param column will be sanitized, removing all but alphanumeric and underscore characters.

func (*Query) Params

func (q *Query) Params() []interface{}

func (*Query) Relations

func (q *Query) Relations(relations ...string) *Query

func (*Query) Select

func (q *Query) Select(structOrSlice interface{}) error

Select executes a query and stores the result in structOrSlice, which should be either a pointer to a struct (if you want a single result) or a slice of pointers if you want multiple results. Returns an error if there is one.

Example:

user := User{} err := query.Select(&user)

or

var users []*User err := query.Select(&users)

func (*Query) Where

func (q *Query) Where(col, op string, val interface{}) *Query

func (*Query) WhereClause

func (q *Query) WhereClause() string

func (*Query) WhereIn

func (q *Query) WhereIn(col string, vals ...interface{}) *Query

func (*Query) WhereNotIn

func (q *Query) WhereNotIn(col string, vals ...interface{}) *Query

type SchemaMigration

type SchemaMigration struct {
	Version    string
	StartedAt  time.Time
	FinishedAt time.Time
}

SchemaMigration represents a schema migration record from the database. These tell us which migrations have been run. This model is read-only.

func SchemaMigrationSelect

func SchemaMigrationSelect(query *Query) ([]*SchemaMigration, error)

SchemaMigrationSelect returns all records matching the query.

type SortParam

type SortParam struct {
	Column    string
	Direction string // only asc and desc allowed
}

func NewSortParam

func NewSortParam(value string) *SortParam

NewSortParam creates a new sort parameter to add to a query.

Sorts will appear on the query string like this:

sort=updated_at__desc&sort=user_id__asc

That means sort first by updated_at descending, then by user_id ascending. If sort direction is missing or invalid, it defaults to asc.

type StorageOption

type StorageOption struct {
	BaseModel
	Provider       string    `json:"provider"`
	Service        string    `json:"service"`
	Region         string    `json:"region"`
	Name           string    `json:"name"`
	CostGBPerMonth float64   `json:"cost_gb_per_month"`
	Comment        string    `json:"comment"`
	UpdatedAt      time.Time `json:"updated_at"`
}

StorageOption contains information about APTrust storage option costs. This is used mainly in monthly cost reporting.

func StorageOptionByID

func StorageOptionByID(id int64) (*StorageOption, error)

StorageOptionByID returns the option with the specified id. Returns pg.ErrNoRows if there is no match.

func StorageOptionByName

func StorageOptionByName(name string) (*StorageOption, error)

func StorageOptionGet

func StorageOptionGet(query *Query) (*StorageOption, error)

StorageOptionGet returns the first option matching the query.

func StorageOptionGetAll

func StorageOptionGetAll() ([]*StorageOption, error)

func StorageOptionSelect

func StorageOptionSelect(query *Query) ([]*StorageOption, error)

StorageOptionSelect returns all options matching the query.

func (*StorageOption) Save

func (option *StorageOption) Save() error

Save saves this StorageOption to the database. This will peform an insert if StorageOption.ID is zero. Otherwise, it updates.

func (*StorageOption) Validate

func (option *StorageOption) Validate() *common.ValidationError

Validate validates the model. This is called automatically on insert and update.

type StorageRecord

type StorageRecord struct {
	ID            int64        `json:"id" pg:"id"`
	GenericFileID int64        `json:"generic_file_id"`
	URL           string       `json:"url" form:"url" pg:"url"`
	GenericFile   *GenericFile `json:"-" pg:"rel:has-one"`
}

func RandomStorageRecord

func RandomStorageRecord() *StorageRecord

RandomStorageRecord() returns a random storage record. Caller should set GenericFileID.

func StorageRecordByID

func StorageRecordByID(id int64) (*StorageRecord, error)

StorageRecordByID returns the file with the specified id. Returns pg.ErrNoRows if there is no match.

func StorageRecordGet

func StorageRecordGet(query *Query) (*StorageRecord, error)

StorageRecordGet returns the first file matching the query.

func StorageRecordSelect

func StorageRecordSelect(query *Query) ([]*StorageRecord, error)

StorageRecordSelect returns all files matching the query.

func (*StorageRecord) GetID

func (sr *StorageRecord) GetID() int64

func (*StorageRecord) Save

func (sr *StorageRecord) Save() error

Save saves this file to the database. This will peform an insert if StorageRecord.ID is zero. Otherwise, it updates.

func (*StorageRecord) Validate

func (sr *StorageRecord) Validate() *common.ValidationError

type TimestampModel

type TimestampModel struct {
	BaseModel
	CreatedAt time.Time `bun:",nullzero" json:"created_at,omitempty"`
	UpdatedAt time.Time `bun:",nullzero" json:"updated_at,omitempty"`
}

func (*TimestampModel) SetTimestamps

func (tsm *TimestampModel) SetTimestamps()

type User

type User struct {
	TimestampModel

	// Name is the user's display name.
	Name string `json:"name" pg:"name"`

	// Email is used to login. Must be unique.
	Email string `json:"email" pg:"email"`

	// PhoneNumber should start with + country code (e.g. +1 for US).
	// This number is used for SMS two-factor auth, so it should be
	// a mobile phone.
	PhoneNumber string `json:"phone_number" pg:"phone_number"`

	// EncryptedPassword is the user's password, encrypted.
	EncryptedPassword string `json:"-" form:"-" pg:"encrypted_password"`

	// ResetPasswordToken is an encrypted version of the token sent to a
	// user who wants to reset their password. The plaintext version of
	// this is emailed to the user.
	ResetPasswordToken string `json:"-" form:"-" pg:"reset_password_token"`

	// ResetPasswordSentAt is a timestamp describing when we sent a password
	// reset email. The ResetPasswordToken should be valid for only a few
	// minutes after this timestamp.
	ResetPasswordSentAt time.Time `json:"reset_password_sent_at" form:"-" pg:"reset_password_sent_at"`

	// RememberCreatedAt - ??? - Legacy field from Devise, probably related
	// to time-based OTPs. Not used.
	RememberCreatedAt time.Time `json:"-" form:"-" pg:"remember_created_at"`

	// SignInCount is the number of times this user has successfully signed in.
	SignInCount int `json:"sign_in_count" form:"-" pg:"sign_in_count,use_zero"`

	// CurrentSignInAt is a timestamp describing when this user signed
	// in for their current session.
	CurrentSignInAt time.Time `json:"current_sign_in_at" form:"-" pg:"current_sign_in_at"`

	// LastSignInAt is a timestamp describing when this user signed
	// in for their previous session.
	LastSignInAt time.Time `json:"last_sign_in_at" form:"-" pg:"last_sign_in_at"`

	// CurrentSignInIP is the IP address from which user signed in.
	CurrentSignInIP string `json:"current_sign_in_ip" form:"-" pg:"current_sign_in_ip"`

	// LastSignInIP is the IP address from which user signed in for their
	// prior session.
	LastSignInIP string `json:"last_sign_in_ip" form:"-" pg:"last_sign_in_ip"`

	// InstitituionID is the id of the institution to which this user
	// belongs.
	InstitutionID int64 `json:"institution_id" pg:"institution_id"`

	// EncryptedAPISecretKey is the user's encrypted API key. This may
	// be empty if the user has never requested a key.
	EncryptedAPISecretKey string `json:"-" form:"-" pg:"encrypted_api_secret_key"`

	// PasswordChangedAt is the date and time the user last changed their
	// password.
	PasswordChangedAt time.Time `json:"password_changed_at" form:"-" pg:"password_changed_at"`

	// EncryptedOTPSecret is the encrypted version of a user's one-time
	// password. The plaintext is usually a six-digit code sent via SMS.
	// This will be empty if we didn't send a code, or if the user has
	// correctly entered it and completed two-factor login.
	EncryptedOTPSecret string `json:"-" form:"-" pg:"encrypted_otp_secret"`

	// EncryptedOTPSecretIV is a legacy field from Devise. Not used.
	// TODO: Delete this.
	EncryptedOTPSecretIV string `json:"-" form:"-" pg:"encrypted_otp_secret_iv"`

	// EncryptedOTPSecretSalt is a legacy field from Devise. Not used.
	EncryptedOTPSecretSalt string `json:"-" form:"-" pg:"encrypted_otp_secret_salt"`

	// EncryptedOTPSentAt describes when we sent a one-time password
	// via text/SMS. We track this because these passwords should be
	// valid only for a limited time. This field be empty except when
	// we're waiting for a user to enter a text/SMS OTP.
	EncryptedOTPSentAt time.Time `json:"-" form:"-" pg:"encrypted_otp_sent_at"`

	// ConsumedTimestep is a legacy field from Devise, which used it
	// for time-based one-time passwords. Not used.
	// TODO: Delete this.
	ConsumedTimestep int `json:"-" form:"-" pg:"consumed_timestep"`

	// OTPRequiredForLogin indicates whether, as a matter of policy, the
	// user must use some form of OTP to log in. If true, the user should
	// be allowed to log in only with Authy one-touch, six-digit SMS
	// code, or backup code.
	//
	// Use IsTwoFactorUser() to actually determine whether we should
	// force this user to use Authy, SMS, or backup code to get it.
	// This is messy because it has to mirror the legacy Rails implementation
	// for now.
	OTPRequiredForLogin bool `json:"otp_required_for_login" pg:"otp_required_for_login"`

	// DeactivatedAt is a timestamp describing when this user was
	// deactivated. For most users, it will be empty. APTrust admin and
	// Institutional Admin can disable other users. Then can also
	// re-enable a user by clearing this flag.
	DeactivatedAt time.Time `json:"deactivated_at" form:"-" pg:"deactivated_at"`

	// EnabledTwoFactor indicates whether the user's account has
	// enabled two factor authentication. See also ConfirmedTwoFactor.
	EnabledTwoFactor bool `json:"enabled_two_factor" form:"-" pg:"enabled_two_factor"`

	// ConfirmedTwoFactor indicates that the system has confirmed this
	// user's phone number (for SMS) and Authy account (for Authy 2FA).
	ConfirmedTwoFactor bool `json:"confirmed_two_factor" form:"-" pg:"confirmed_two_factor"`

	// OTPBackupCodes is a list of backup codes the user can use to
	// log if they can't get in via SMS or Authy.
	OTPBackupCodes []string `json:"-" form:"-" pg:"otp_backup_codes,array"`

	// AuthyID is the user's Authy ID. We need this to send them push
	// messages to complete one-touch sign-in. This will be empty for
	// those who don't use Authy.
	AuthyID string `json:"-" form:"-" pg:"authy_id"`

	// LastSignInWithAuthy is the timestamp of this user's last
	// successful sign-in with Authy.
	LastSignInWithAuthy time.Time `json:"last_sign_in_with_authy" form:"-" pg:"last_sign_in_with_authy"`

	// AuthyStatus indicates how the user wants to authenticate with
	// Authy. If it's constants.TwoFactorAuthy, we should send them a
	// push, so they can login with one-touch. Anything else means SMS,
	// but call IsTwoFactorUser() to make sure they're actually require
	// two-factor auth before trying to text them.
	AuthyStatus string `json:"authy_status" pg:"authy_status"`

	// EmailVerified will be true once the system has verified that the
	// user's email address is correct.
	EmailVerified bool `json:"email_verified" form:"-" pg:"email_verified"`

	// InitialPasswordUpdated will be true once a user updates their
	// initial password. When we create a new user, we generate a random
	// password, then send them an email with a login link. At that
	// point, the user has to set their own password.
	InitialPasswordUpdated bool `json:"initial_password_updated" form:"-" pg:"initial_password_updated"`

	// ForcePasswordUpdate indicated whether the user will be forced to
	// update their password the next time they visit.
	ForcePasswordUpdate bool `json:"force_password_update" form:"-" pg:"force_password_update"`

	// GracePeriod is a legacy field from the old Rails app. It held the
	// date by which a user MUST complete either Authy or SMS two-factor
	// setup. This feature was universally despised, and we won't be
	// using it unless someone complains. For now, consider this as
	// unused.
	GracePeriod time.Time `json:"grace_period" time_format:"2006-01-02" pg:"grace_period"`

	// AwaitingSecondFactor indicates that the user has logged in with
	// email and password, but has not yet completed the second login
	// step (Authy, SMS OTP, or backup code). This flag is only set on
	// users going through the two-factor process. Middleware checks it
	// to prevent partially logged-in users from accessing any pages other
	// than those required to complete the two-factor login process.
	AwaitingSecondFactor bool `json:"-" pg:"awaiting_second_factor,use_zero"`

	// Role is the user's role.
	Role string `json:"role" pg:"role"`

	// Institution is where they lock you up after you've spent too much
	// time trying to figure out the old Rails code.
	Institution *Institution `json:"institution" pg:"rel:has-one"`
}

User is a person who can log in and do stuff. Most of this model was inherited from the old Rails app. It includes a number of obsolete fields that we should remove after we're stable in production.

func UserByEmail

func UserByEmail(email string) (*User, error)

UserByEmail returns the user with the specified email address. Returns pg.ErrNoRows if there is no match.

func UserByID

func UserByID(id int64) (*User, error)

UserByID returns the institution with the specified id. Returns pg.ErrNoRows if there is no match.

func UserGet

func UserGet(query *Query) (*User, error)

UserGet returns the first user matching the query.

func UserSelect

func UserSelect(query *Query) ([]*User, error)

UserSelect returns all user matching the query.

func UserSignIn

func UserSignIn(email, password, ipAddr string) (*User, error)

UserSignIn signs a user in. If successful, it returns the User record with User.Institution properly set. If it fails, check the error.

func (*User) ClearOTPSecret

func (user *User) ClearOTPSecret() error

ClearOTPSecret deletes the user's EncryptedOTPSecret.

func (*User) CountryCodeAndPhone

func (user *User) CountryCodeAndPhone() (int32, string, error)

CountryCodeAndPhone returns this user's country code and phone number.

func (*User) CreateOTPToken

func (user *User) CreateOTPToken() (string, error)

CreateOTPToken creates a new one-time password token, typically used for SMS-based two-factor authentication. It saves an encrypted version of the token to the database and returns the plaintext version of the token. For SMS, we use six-digit tokens because they're easy for a user to type.

func (*User) Delete

func (user *User) Delete() error

func (*User) HasPermission

func (user *User) HasPermission(action constants.Permission, institutionID int64) bool

HasPermission returns true or false to indicate whether the user has sufficient permissions to perform the requested action. Param action should be one of the constants from constants/permissions.go. Param institutionID should be the ID of the institution that owns the object upon which the user is trying to act. In certain cases, such as when a user is editing him/herself, this can be zero.

func (*User) IsAdmin

func (user *User) IsAdmin() bool

IsAdmin returns true if user is a Sys Admin. Returns false for all other roles (including institutional admin, which is not a super user).

func (*User) IsAuthyOneTouchUser

func (user *User) IsAuthyOneTouchUser() bool

IsAuthyOneTouchUser returns true if the user has enabled Authy one touch for two-factor login.

func (*User) IsSMSUser

func (user *User) IsSMSUser() bool

IsSMSUser returns true if this user has enabled two-factor authentication with SMS/text message.

func (*User) IsTwoFactorUser

func (user *User) IsTwoFactorUser() bool

IsTwoFactorUser returns true if this user has enabled and confirmed two factor authentication.

Sorry... For now, we have to work with the convoluted logic of the old Rails app. Hence the confusion between this and OTPRequiredForLogin.

func (*User) Save

func (user *User) Save() error

func (*User) SignOut

func (user *User) SignOut() error

UserSignOut signs a user out.

func (*User) ToMin

func (user *User) ToMin() *UserMin

ToMin returns a UserMin struct containing only enough info to identify a user. This subset of info is safe to expose through member API endpoints.

func (*User) TwoFactorMethod

func (user *User) TwoFactorMethod() string

TwoFactorMethod returns one of the following:

constants.TwoFactorNone if the user does not use two-factor auth.

constants.TwoFactorAuthy if the user uses two-factor auth via Authy.

constants.TwoFactorSMS if the user receives two-factor OTP code via text/SMS

func (*User) Undelete

func (user *User) Undelete() error

func (*User) Validate

func (user *User) Validate() *common.ValidationError

type UserMin

type UserMin struct {
	ID    int64  `json:"id"`
	Name  string `json:"name"`
	Email string `json:"email"`
}

UserMin contains minimum required information to identify a user. This is used for serialization in parts of the member API when we want to show which user is attached to a record (such as a deletion request) but we do not want to expose unnecessary information about that user.

type UserView

type UserView struct {
	ID                     int64     `json:"id" pg:"id"`
	Name                   string    `json:"name" pg:"name"`
	Email                  string    `json:"email" pg:"email"`
	PhoneNumber            string    `json:"phone_number" pg:"phone_number"`
	CreatedAt              time.Time `json:"created_at" pg:"created_at"`
	UpdatedAt              time.Time `json:"updated_at" pg:"updated_at"`
	ResetPasswordSentAt    time.Time `json:"reset_password_sent_at" pg:"reset_password_sent_at"`
	RememberCreatedAt      time.Time `json:"-" pg:"remember_created_at"`
	SignInCount            int       `json:"sign_in_count" pg:"sign_in_count,use_zero"`
	CurrentSignInAt        time.Time `json:"current_sign_in_at" pg:"current_sign_in_at"`
	LastSignInAt           time.Time `json:"last_sign_in_at" pg:"last_sign_in_at"`
	CurrentSignInIP        string    `json:"current_sign_in_ip" pg:"current_sign_in_ip"`
	LastSignInIP           string    `json:"last_sign_in_ip" pg:"last_sign_in_ip"`
	InstitutionID          int64     `json:"institution_id" pg:"institution_id"`
	PasswordChangedAt      time.Time `json:"password_changed_at" pg:"password_changed_at"`
	ConsumedTimestep       int       `json:"-" pg:"consumed_timestep"`
	OTPRequiredForLogin    bool      `json:"otp_required_for_login" pg:"otp_required_for_login"`
	DeactivatedAt          time.Time `json:"deactivated_at" pg:"deactivated_at"`
	EnabledTwoFactor       bool      `json:"enabled_two_factor" pg:"enabled_two_factor"`
	ConfirmedTwoFactor     bool      `json:"confirmed_two_factor" pg:"confirmed_two_factor"`
	AuthyID                string    `json:"-" pg:"authy_id"`
	LastSignInWithAuthy    time.Time `json:"last_sign_in_with_authy" pg:"last_sign_in_with_authy"`
	AuthyStatus            string    `json:"authy_status" pg:"authy_status"`
	EmailVerified          bool      `json:"email_verified" pg:"email_verified"`
	InitialPasswordUpdated bool      `json:"initial_password_updated" pg:"initial_password_updated"`
	ForcePasswordUpdate    bool      `json:"force_password_update" pg:"force_password_update"`
	GracePeriod            time.Time `json:"grace_period" pg:"grace_period"`
	Role                   string    `json:"role" pg:"role"`
	InstitutionName        string    `json:"institution_name" pg:"institution_name"`
	InstitutionIdentifier  string    `json:"institution_identifier" pg:"institution_identifier"`
	InstitutionState       string    `json:"institution_state" pg:"institution_state"`
	InstitutionType        string    `json:"institution_type" pg:"institution_type"`
	MemberInstitutionId    int64     `json:"member_institution_id" pg:"member_institution_id"`

	MemberInstitutionName       string `json:"member_institution_name" pg:"member_institution_name"`
	MemberInstitutionIdentifier string `json:"member_institution_identifier" pg:"member_institution_identifier"`
	MemberInstitutionState      string `json:"member_institution_state" pg:"member_institution_state"`

	OTPEnabled      bool   `json:"otp_enabled" pg:"otp_enabled"`
	ReceivingBucket string `json:"receiving_bucket" pg:"receiving_bucket"`
	RestoreBucket   string `json:"restore_bucket" pg:"restore_bucket"`
	// contains filtered or unexported fields
}

func UserViewByEmail

func UserViewByEmail(email string) (*UserView, error)

UserViewByEmail returns the UserView record with the specified email address. Returns pg.ErrNoRows if there is no match.

func UserViewByID

func UserViewByID(id int64) (*UserView, error)

UserViewByID returns the UserView record with the specified id. Returns pg.ErrNoRows if there is no match.

func UserViewGet

func UserViewGet(query *Query) (*UserView, error)

UserViewGet returns the first user view record matching the query.

func UserViewSelect

func UserViewSelect(query *Query) ([]*UserView, error)

UserViewSelect returns all UserView records matching the query. This read-only view is for listing users. It includes a number of insitition fields to simplify the search process.

type WorkItem

type WorkItem struct {
	TimestampModel
	Name                 string    `json:"name" pg:"name"`
	ETag                 string    `json:"etag" pg:"etag"`
	InstitutionID        int64     `json:"institution_id"`
	IntellectualObjectID int64     `json:"intellectual_object_id"`
	GenericFileID        int64     `json:"generic_file_id"`
	Bucket               string    `json:"bucket"`
	User                 string    `json:"user"`
	Note                 string    `json:"note"`
	Action               string    `json:"action"`
	Stage                string    `json:"stage"`
	Status               string    `json:"status"`
	Outcome              string    `json:"outcome"`
	BagDate              time.Time `json:"bag_date"`
	DateProcessed        time.Time `json:"date_processed"`
	Retry                bool      `json:"retry" pg:",use_zero"`
	Node                 string    `json:"node"`
	PID                  int       `json:"pid"`
	NeedsAdminReview     bool      `json:"needs_admin_review" pg:",use_zero"`
	QueuedAt             time.Time `json:"queued_at"`
	Size                 int64     `json:"size"`
	StageStartedAt       time.Time `json:"stage_started_at"`
	APTrustApprover      string    `json:"aptrust_approver" pg:"aptrust_approver"`
	InstApprover         string    `json:"inst_approver"`
}

WorkItem contains information about a task or suite of related tasks to be performed by the preservation services workers, such as ingest, restoration, and deletion. While preservation services uses Redis to track interim processing data as it works, WorkItem records here in the registry keep a record that's visible to both depositors and APTrust admins.

These high-level records let us know whether a task is pending, in process, or completed. They also let us know the outcome and what specific errors may have occurred.

WorkItems cannot be deleted because they're part of our system's audit trail.

func LastSuccessfulIngest

func LastSuccessfulIngest(objID int64) (*WorkItem, error)

LastSuccessfulIngest returns the last successful ingest WorkItem for the specified intellectual object id.

func NewDeletionItem

func NewDeletionItem(obj *IntellectualObject, gf *GenericFile, requestedBy, approvedBy *User) (*WorkItem, error)

NewDeletionItem creates a new work item to delete a file or object. Param obj is required. If gf is not nil, this will create a WorkItem to delete file gf. Otherwise, it creates a WorkItem to delete object obj.

Param requestedBy is the User who initially requested the deletion. Param approvedBy is the User who approved the deletion request. These two are required.

func NewItemFromLastSuccessfulIngest

func NewItemFromLastSuccessfulIngest(objID int64) (*WorkItem, error)

NewItemFromLastSuccessfulIngest creates a new WorkItem based on the last successful ingest WorkItem of the specified object. This is used for creating various deletion and restoration WorkItems. The returned WorkItem will include the proper object name, object id, object identifier and etag. All other fields will be cleared out. The caller must set essential fields like Action, User, GenericFileID (if appropriate) and the like.

This will return an error if the system can't find the last successful ingest record for the specified object.

func NewRestorationItem

func NewRestorationItem(obj *IntellectualObject, gf *GenericFile, user *User) (*WorkItem, error)

NewRestorationItem creates and saves a new WorkItem for an object or file restoration.

Param obj (required) is the object to be restored. gf is the GenericFile to be restored. This can be zero if we're restoring an object instead of a file. Param user is the user initiating the restoration.

Before creating a restoration WorkItem, the caller should ensure that the object and file have no pending work items. See WorkItemsPendingForObject() and WorkItemsPendinForFile().

func RandomWorkItem

func RandomWorkItem(name, action string, objID, gfID int64) *WorkItem

func WorkItemByID

func WorkItemByID(id int64) (*WorkItem, error)

WorkItemByID returns the work item with the specified id. Returns pg.ErrNoRows if there is no match.

func WorkItemGet

func WorkItemGet(query *Query) (*WorkItem, error)

WorkItemGet returns the first work item matching the query.

func WorkItemSelect

func WorkItemSelect(query *Query) ([]*WorkItem, error)

WorkItemSelect returns all work items matching the query.

func WorkItemsPendingForFile

func WorkItemsPendingForFile(fileID int64) ([]*WorkItem, error)

WorkItemsPendingForFile returns a list of in-progress WorkItems for the GenericFile with the specified ID.

func WorkItemsPendingForObject

func WorkItemsPendingForObject(instID int64, bagName string) ([]*WorkItem, error)

WorkItemsPendingForObject returns a list of in-progress WorkItems for the IntellectualObject with the specified institution ID and bag name. We don't use an IntellectualObjectID here because when we're ingesting or re-ingesting an object, the WorkItem won't have an ObjectID until the ingest/re-ingest is complete.

This method is called before initializing a new restoration or deletion request. We specifically want to avoid the case where a user requests a restoration or deletion on an object that is about to be reingested. If that were to happen, the delete worker would be deleting files that an ingest worker just wrote. Or the ingest worker would be overwriting files that the restore worker was trying to restore.

Pharos queried by object id, which was a mistake that would not catch re-ingests. This corrects that.

func (*WorkItem) AlertOnSuccessfulSpotTest

func (item *WorkItem) AlertOnSuccessfulSpotTest() *Alert

AlertOnSuccessfulSpotTest sends an email to institutional users and admins when a restoration spot test has completed. It's the institution's job to figure out what to do with the restored object.

This returns an alert if the alert was created successfully, nil otherwise. Zero does not necessarily indicate failure. It just means we didn't create an alert, and there may be valid reasons for not doing so.

func (*WorkItem) GetSpotTestDetails

func (item *WorkItem) GetSpotTestDetails() (*Institution, *IntellectualObject, error)

GetSpotTestDetails returns true if this WorkItem is a restoration spot test. It also returns the Institution on whose behalf the test was conducted, and the object that was or is being restored.

func (*WorkItem) HasCompleted

func (item *WorkItem) HasCompleted() bool

HasCompleted returns true if this item has completed processing.

func (*WorkItem) Save

func (item *WorkItem) Save() error

Save saves this work item to the database. This will peform an insert if WorkItem.ID is zero. Otherwise, it updates.

func (*WorkItem) SetForRequeue

func (item *WorkItem) SetForRequeue(stage string) error

SetForRequeue sets properies so this item can be requeued. Note that it saves the object. It will return constants.ErrInvalidRequeue if the stage is not valid, and may return validation or pg error if the object cannot be saved.

The call is responsible for actually pushing the WorkItem.ID into the correct NSQ topic.

func (*WorkItem) Validate

func (item *WorkItem) Validate() *common.ValidationError

func (*WorkItem) ValidateChanges

func (item *WorkItem) ValidateChanges(updatedItem *WorkItem) error

ValidateChanges returns an error if updatedItem contains illegal changes. Don't change action on work items. Create a new work item instead. Changing any of the other IDs or identifiers leads to incorrect data, so it's prohibited.

type WorkItemCount

type WorkItemCount struct {
	InstitutionID int64  `json:"institution_id"`
	RowCount      int    `json:"row_count"`
	Action        string `json:"action"`
	// contains filtered or unexported fields
}

type WorkItemView

type WorkItemView struct {
	ID                       int64     `json:"id" pg:"id"`
	Name                     string    `json:"name" pg:"name"`
	ETag                     string    `json:"etag" pg:"etag"`
	InstitutionID            int64     `json:"institution_id" pg:"institution_id"`
	InstitutionName          string    `json:"institution_name" pg:"institution_name"`
	InstitutionIdentifier    string    `json:"institution_identifier" pg:"institution_identifier"`
	IntellectualObjectID     int64     `json:"intellectual_object_id" pg:"intellectual_object_id"`
	ObjectIdentifier         string    `json:"object_identifier" pg:"object_identifier"`
	AltIdentifier            string    `json:"alt_identifier" pg:"alt_identifier"`
	BagGroupIdentifier       string    `json:"bag_group_identifier" pg:"bag_group_identifier"`
	StorageOption            string    `json:"storage_option" pg:"storage_option"`
	BagItProfileIdentifier   string    `json:"bagit_profile_identifier" pg:"bagit_profile_identifier"`
	SourceOrganization       string    `json:"source_organization" pg:"source_organization"`
	InternalSenderIdentifier string    `json:"internal_sender_identifier" pg:"internal_sender_identifier"`
	GenericFileID            int64     `json:"generic_file_id" pg:"generic_file_id"`
	GenericFileIdentifier    string    `json:"generic_file_identifier" pg:"generic_file_identifier"`
	Bucket                   string    `json:"bucket" pg:"bucket"`
	User                     string    `json:"user" pg:"user"`
	Note                     string    `json:"note" pg:"note"`
	Action                   string    `json:"action" pg:"action"`
	Stage                    string    `json:"stage" pg:"stage"`
	Status                   string    `json:"status" pg:"status"`
	Outcome                  string    `json:"outcome" pg:"outcome"`
	BagDate                  time.Time `json:"bag_date" pg:"bag_date"`
	DateProcessed            time.Time `json:"date_processed" pg:"date_processed"`
	Retry                    bool      `json:"retry" pg:"retry"`
	Node                     string    `json:"node" pg:"node"`
	PID                      int       `json:"pid" pg:"pid"`
	NeedsAdminReview         bool      `json:"needs_admin_review" pg:"needs_admin_review"`
	QueuedAt                 time.Time `json:"queued_at" pg:"queued_at"`
	Size                     int64     `json:"size" pg:"size"`
	StageStartedAt           time.Time `json:"stage_started_at" pg:"stage_started_at"`
	APTrustApprover          string    `json:"aptrust_approver" pg:"aptrust_approver"`
	InstApprover             string    `json:"inst_approver" pg:"inst_approver"`
	CreatedAt                time.Time `json:"created_at" pg:"created_at"`
	UpdatedAt                time.Time `json:"updated_at" pg:"updated_at"`
	// contains filtered or unexported fields
}

WorkItemView is a read-only model for querying. It flattens out WorkItem and some of its one-to-one relations for easy querying.

func WorkItemViewByID

func WorkItemViewByID(id int64) (*WorkItemView, error)

WorkItemViewByID returns the work item with the specified id. Returns pg.ErrNoRows if there is no match.

func WorkItemViewGet

func WorkItemViewGet(query *Query) (*WorkItemView, error)

WorkItemViewGet returns the first work item matching the query.

func WorkItemViewSelect

func WorkItemViewSelect(query *Query) ([]*WorkItemView, error)

WorkItemViewSelect returns all work items matching the query.

func (*WorkItemView) FindIngestedObject

func (item *WorkItemView) FindIngestedObject() (*IntellectualObject, error)

FindIngestedObject sounds like something the vet would do with your cat. It's actually used to fix a specific case where Registry thinks an ingest has failed, even though it actually succeeded. If we have to manually push the WorkItem to Ingest/Cleanup/Success, we need to link it to the ingested object. That object should match the identifier and etag of the WorkItem.

We rarely need to do this. This case occurs only when preserv tries to re-record an already completed ingest and accidentally overwrites the WorkItem's object id. We're still trying to track down the conditions that lead to that event, but again, it's rare.

func (*WorkItemView) GetID

func (item *WorkItemView) GetID() int64

GetID returns the ID of this WorkItem.

func (*WorkItemView) GetObjIdentifier

func (item *WorkItemView) GetObjIdentifier() string

ObjIdentifier returns this item's ObjectIdentifier if it exists, or what the object identifier would be if the item were fully ingested. Note that for ingest items, no object identifier is assigned until ingest is complete. However, to get a look at preservation services' internal Redis data, we need to know this not-yet-assigned identifier to compose the Redis key.

func (*WorkItemView) HasCompleted

func (item *WorkItemView) HasCompleted() bool

HasCompleted returns true if this item has completed processing.

func (*WorkItemView) IngestObjectLinkIsMissing

func (item *WorkItemView) IngestObjectLinkIsMissing() bool

IngestObjectLinkIsMissing returns true if this WorkItemView should have an associated IntellectualObjectID but does not have it.

func (*WorkItemView) Validate

func (item *WorkItemView) Validate() *common.ValidationError

Validate is a no-op. This view is not writable, so we can't save to it. This method is here to satisfy the Model interface.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL