list, set and get pinned notices

This commit is contained in:
Henry 2021-02-23 20:23:50 +01:00
parent e5a07fd8bc
commit 1ec3e8b064
19 changed files with 1775 additions and 1116 deletions

View File

@ -48,14 +48,19 @@ type AllowListService interface {
type AliasService interface{}
// PinnedNoticesService allows an admin to assign Notices to specific placeholder pages.
// TODO: better name then _fixed_
// like updates, privacy policy, code of conduct
// TODO: enum these
type PinnedNoticesService interface {
// List returns a list of all the pinned notices with their corrosponding notices and languges
List(context.Context) (PinnedNotices, error)
// Set assigns a fixed page name to an page ID and a language to allow for multiple translated versions of the same page.
Set(name PinnedNoticeName, id int64, lang string) error
Set(ctx context.Context, name PinnedNoticeName, id int64) error
// Get returns a single notice for a name and a language
Get(ctx context.Context, name PinnedNoticeName, language string) (*Notice, error)
}
// NoticesService is the low level store to manage single notices
type NoticesService interface {
// GetByID returns the page for that ID or an error
GetByID(context.Context, int64) (Notice, error)

View File

@ -2,18 +2,47 @@
package mockdb
import (
"context"
"sync"
"github.com/ssb-ngi-pointer/go-ssb-room/admindb"
)
type FakePinnedNoticesService struct {
SetStub func(admindb.PinnedNoticeName, int64, string) error
GetStub func(context.Context, admindb.PinnedNoticeName, string) (*admindb.Notice, error)
getMutex sync.RWMutex
getArgsForCall []struct {
arg1 context.Context
arg2 admindb.PinnedNoticeName
arg3 string
}
getReturns struct {
result1 *admindb.Notice
result2 error
}
getReturnsOnCall map[int]struct {
result1 *admindb.Notice
result2 error
}
ListStub func(context.Context) (admindb.PinnedNotices, error)
listMutex sync.RWMutex
listArgsForCall []struct {
arg1 context.Context
}
listReturns struct {
result1 admindb.PinnedNotices
result2 error
}
listReturnsOnCall map[int]struct {
result1 admindb.PinnedNotices
result2 error
}
SetStub func(context.Context, admindb.PinnedNoticeName, int64) error
setMutex sync.RWMutex
setArgsForCall []struct {
arg1 admindb.PinnedNoticeName
arg2 int64
arg3 string
arg1 context.Context
arg2 admindb.PinnedNoticeName
arg3 int64
}
setReturns struct {
result1 error
@ -25,13 +54,143 @@ type FakePinnedNoticesService struct {
invocationsMutex sync.RWMutex
}
func (fake *FakePinnedNoticesService) Set(arg1 admindb.PinnedNoticeName, arg2 int64, arg3 string) error {
func (fake *FakePinnedNoticesService) Get(arg1 context.Context, arg2 admindb.PinnedNoticeName, arg3 string) (*admindb.Notice, error) {
fake.getMutex.Lock()
ret, specificReturn := fake.getReturnsOnCall[len(fake.getArgsForCall)]
fake.getArgsForCall = append(fake.getArgsForCall, struct {
arg1 context.Context
arg2 admindb.PinnedNoticeName
arg3 string
}{arg1, arg2, arg3})
stub := fake.GetStub
fakeReturns := fake.getReturns
fake.recordInvocation("Get", []interface{}{arg1, arg2, arg3})
fake.getMutex.Unlock()
if stub != nil {
return stub(arg1, arg2, arg3)
}
if specificReturn {
return ret.result1, ret.result2
}
return fakeReturns.result1, fakeReturns.result2
}
func (fake *FakePinnedNoticesService) GetCallCount() int {
fake.getMutex.RLock()
defer fake.getMutex.RUnlock()
return len(fake.getArgsForCall)
}
func (fake *FakePinnedNoticesService) GetCalls(stub func(context.Context, admindb.PinnedNoticeName, string) (*admindb.Notice, error)) {
fake.getMutex.Lock()
defer fake.getMutex.Unlock()
fake.GetStub = stub
}
func (fake *FakePinnedNoticesService) GetArgsForCall(i int) (context.Context, admindb.PinnedNoticeName, string) {
fake.getMutex.RLock()
defer fake.getMutex.RUnlock()
argsForCall := fake.getArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3
}
func (fake *FakePinnedNoticesService) GetReturns(result1 *admindb.Notice, result2 error) {
fake.getMutex.Lock()
defer fake.getMutex.Unlock()
fake.GetStub = nil
fake.getReturns = struct {
result1 *admindb.Notice
result2 error
}{result1, result2}
}
func (fake *FakePinnedNoticesService) GetReturnsOnCall(i int, result1 *admindb.Notice, result2 error) {
fake.getMutex.Lock()
defer fake.getMutex.Unlock()
fake.GetStub = nil
if fake.getReturnsOnCall == nil {
fake.getReturnsOnCall = make(map[int]struct {
result1 *admindb.Notice
result2 error
})
}
fake.getReturnsOnCall[i] = struct {
result1 *admindb.Notice
result2 error
}{result1, result2}
}
func (fake *FakePinnedNoticesService) List(arg1 context.Context) (admindb.PinnedNotices, error) {
fake.listMutex.Lock()
ret, specificReturn := fake.listReturnsOnCall[len(fake.listArgsForCall)]
fake.listArgsForCall = append(fake.listArgsForCall, struct {
arg1 context.Context
}{arg1})
stub := fake.ListStub
fakeReturns := fake.listReturns
fake.recordInvocation("List", []interface{}{arg1})
fake.listMutex.Unlock()
if stub != nil {
return stub(arg1)
}
if specificReturn {
return ret.result1, ret.result2
}
return fakeReturns.result1, fakeReturns.result2
}
func (fake *FakePinnedNoticesService) ListCallCount() int {
fake.listMutex.RLock()
defer fake.listMutex.RUnlock()
return len(fake.listArgsForCall)
}
func (fake *FakePinnedNoticesService) ListCalls(stub func(context.Context) (admindb.PinnedNotices, error)) {
fake.listMutex.Lock()
defer fake.listMutex.Unlock()
fake.ListStub = stub
}
func (fake *FakePinnedNoticesService) ListArgsForCall(i int) context.Context {
fake.listMutex.RLock()
defer fake.listMutex.RUnlock()
argsForCall := fake.listArgsForCall[i]
return argsForCall.arg1
}
func (fake *FakePinnedNoticesService) ListReturns(result1 admindb.PinnedNotices, result2 error) {
fake.listMutex.Lock()
defer fake.listMutex.Unlock()
fake.ListStub = nil
fake.listReturns = struct {
result1 admindb.PinnedNotices
result2 error
}{result1, result2}
}
func (fake *FakePinnedNoticesService) ListReturnsOnCall(i int, result1 admindb.PinnedNotices, result2 error) {
fake.listMutex.Lock()
defer fake.listMutex.Unlock()
fake.ListStub = nil
if fake.listReturnsOnCall == nil {
fake.listReturnsOnCall = make(map[int]struct {
result1 admindb.PinnedNotices
result2 error
})
}
fake.listReturnsOnCall[i] = struct {
result1 admindb.PinnedNotices
result2 error
}{result1, result2}
}
func (fake *FakePinnedNoticesService) Set(arg1 context.Context, arg2 admindb.PinnedNoticeName, arg3 int64) error {
fake.setMutex.Lock()
ret, specificReturn := fake.setReturnsOnCall[len(fake.setArgsForCall)]
fake.setArgsForCall = append(fake.setArgsForCall, struct {
arg1 admindb.PinnedNoticeName
arg2 int64
arg3 string
arg1 context.Context
arg2 admindb.PinnedNoticeName
arg3 int64
}{arg1, arg2, arg3})
stub := fake.SetStub
fakeReturns := fake.setReturns
@ -52,13 +211,13 @@ func (fake *FakePinnedNoticesService) SetCallCount() int {
return len(fake.setArgsForCall)
}
func (fake *FakePinnedNoticesService) SetCalls(stub func(admindb.PinnedNoticeName, int64, string) error) {
func (fake *FakePinnedNoticesService) SetCalls(stub func(context.Context, admindb.PinnedNoticeName, int64) error) {
fake.setMutex.Lock()
defer fake.setMutex.Unlock()
fake.SetStub = stub
}
func (fake *FakePinnedNoticesService) SetArgsForCall(i int) (admindb.PinnedNoticeName, int64, string) {
func (fake *FakePinnedNoticesService) SetArgsForCall(i int) (context.Context, admindb.PinnedNoticeName, int64) {
fake.setMutex.RLock()
defer fake.setMutex.RUnlock()
argsForCall := fake.setArgsForCall[i]
@ -91,6 +250,10 @@ func (fake *FakePinnedNoticesService) SetReturnsOnCall(i int, result1 error) {
func (fake *FakePinnedNoticesService) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
fake.getMutex.RLock()
defer fake.getMutex.RUnlock()
fake.listMutex.RLock()
defer fake.listMutex.RUnlock()
fake.setMutex.RLock()
defer fake.setMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}

View File

@ -35,19 +35,17 @@ type FakeNoticesService struct {
removeIDReturnsOnCall map[int]struct {
result1 error
}
SaveStub func(context.Context, admindb.Notice) (int64, error)
SaveStub func(context.Context, *admindb.Notice) error
saveMutex sync.RWMutex
saveArgsForCall []struct {
arg1 context.Context
arg2 admindb.Notice
arg2 *admindb.Notice
}
saveReturns struct {
result1 int64
result2 error
result1 error
}
saveReturnsOnCall map[int]struct {
result1 int64
result2 error
result1 error
}
invocations map[string][][]interface{}
invocationsMutex sync.RWMutex
@ -180,12 +178,12 @@ func (fake *FakeNoticesService) RemoveIDReturnsOnCall(i int, result1 error) {
}{result1}
}
func (fake *FakeNoticesService) Save(arg1 context.Context, arg2 admindb.Notice) (int64, error) {
func (fake *FakeNoticesService) Save(arg1 context.Context, arg2 *admindb.Notice) error {
fake.saveMutex.Lock()
ret, specificReturn := fake.saveReturnsOnCall[len(fake.saveArgsForCall)]
fake.saveArgsForCall = append(fake.saveArgsForCall, struct {
arg1 context.Context
arg2 admindb.Notice
arg2 *admindb.Notice
}{arg1, arg2})
stub := fake.SaveStub
fakeReturns := fake.saveReturns
@ -195,9 +193,9 @@ func (fake *FakeNoticesService) Save(arg1 context.Context, arg2 admindb.Notice)
return stub(arg1, arg2)
}
if specificReturn {
return ret.result1, ret.result2
return ret.result1
}
return fakeReturns.result1, fakeReturns.result2
return fakeReturns.result1
}
func (fake *FakeNoticesService) SaveCallCount() int {
@ -206,43 +204,40 @@ func (fake *FakeNoticesService) SaveCallCount() int {
return len(fake.saveArgsForCall)
}
func (fake *FakeNoticesService) SaveCalls(stub func(context.Context, admindb.Notice) (int64, error)) {
func (fake *FakeNoticesService) SaveCalls(stub func(context.Context, *admindb.Notice) error) {
fake.saveMutex.Lock()
defer fake.saveMutex.Unlock()
fake.SaveStub = stub
}
func (fake *FakeNoticesService) SaveArgsForCall(i int) (context.Context, admindb.Notice) {
func (fake *FakeNoticesService) SaveArgsForCall(i int) (context.Context, *admindb.Notice) {
fake.saveMutex.RLock()
defer fake.saveMutex.RUnlock()
argsForCall := fake.saveArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeNoticesService) SaveReturns(result1 int64, result2 error) {
func (fake *FakeNoticesService) SaveReturns(result1 error) {
fake.saveMutex.Lock()
defer fake.saveMutex.Unlock()
fake.SaveStub = nil
fake.saveReturns = struct {
result1 int64
result2 error
}{result1, result2}
result1 error
}{result1}
}
func (fake *FakeNoticesService) SaveReturnsOnCall(i int, result1 int64, result2 error) {
func (fake *FakeNoticesService) SaveReturnsOnCall(i int, result1 error) {
fake.saveMutex.Lock()
defer fake.saveMutex.Unlock()
fake.SaveStub = nil
if fake.saveReturnsOnCall == nil {
fake.saveReturnsOnCall = make(map[int]struct {
result1 int64
result2 error
result1 error
})
}
fake.saveReturnsOnCall[i] = struct {
result1 int64
result2 error
}{result1, result2}
result1 error
}{result1}
}
func (fake *FakeNoticesService) Invocations() map[string][][]interface{} {

View File

@ -1,4 +1,10 @@
-- +migrate Up
CREATE TABLE pins (
id integer NOT NULL PRIMARY KEY,
name text NOT NULL UNIQUE
);
CREATE TABLE notices (
id integer PRIMARY KEY AUTOINCREMENT NOT NULL,
title text NOT NULL,
@ -6,17 +12,24 @@ CREATE TABLE notices (
language text NOT NULL
);
CREATE TABLE pinned_notices (
name text NOT NULL,
notice_id integer NOT NULL UNIQUE,
language text NOT NULL,
PRIMARY KEY (name, language),
-- n:m relation table
CREATE TABLE pin_notices (
notice_id integer NOT NULL,
pin_id integer NOT NULL,
-- make sure the notices exist
FOREIGN KEY ( notice_id ) REFERENCES notices( "id" )
PRIMARY KEY (notice_id, pin_id),
FOREIGN KEY ( notice_id ) REFERENCES notices( "id" ),
FOREIGN KEY ( pin_id ) REFERENCES pins( "id" )
);
-- TODO: find a better way to insert the defaults
INSERT INTO pins (name) VALUES
('NoticeDescription'),
('NoticeNews'),
('NoticeCodeOfConduct'),
('NoticePrivacyPolicy');
INSERT INTO notices (title, content, language) VALUES
('Description', 'Basic description of this Room.', 'en-GB'),
('News', 'Some recent updates...', 'en-GB'),
@ -31,14 +44,15 @@ INSERT INTO notices (title, content, language) VALUES
('Datenschutzrichtlinien', 'Bitte aktualisieren', 'de-DE'),
('Beschreibung', 'Allgemeine beschreibung des Raumes.', 'de-DE');
INSERT INTO pinned_notices (name, notice_id, language) VALUES
('NoticeDescription', 1, 'en-GB'),
('NoticeNews', 2, 'en-GB'),
('NoticeCodeOfConduct', 3, 'en-GB'),
('NoticePrivacyPolicy', 4, 'en-GB'),
('NoticePrivacyPolicy', 5, 'de-DE'),
('NoticeDescription', 6, 'de-DE');
INSERT INTO pin_notices (notice_id, pin_id) VALUES
(1, 1),
(2, 2),
(3, 3),
(4, 4),
(5, 4),
(6, 1);
-- +migrate Down
DROP TABLE notices;
DROP TABLE pinned_notices;
DROP TABLE pins;
DROP TABLE pin_notices;

View File

@ -4,13 +4,15 @@
package models
var TableNames = struct {
AllowList string
AuthFallback string
Notices string
PinnedNotices string
AllowList string
AuthFallback string
Notices string
PinNotices string
Pins string
}{
AllowList: "allow_list",
AuthFallback: "auth_fallback",
Notices: "notices",
PinnedNotices: "pinned_notices",
AllowList: "allow_list",
AuthFallback: "auth_fallback",
Notices: "notices",
PinNotices: "pin_notices",
Pins: "pins",
}

View File

@ -59,14 +59,14 @@ var NoticeWhere = struct {
// NoticeRels is where relationship names are stored.
var NoticeRels = struct {
PinnedNotice string
Pins string
}{
PinnedNotice: "PinnedNotice",
Pins: "Pins",
}
// noticeR is where relationships are stored.
type noticeR struct {
PinnedNotice *PinnedNotice `boil:"PinnedNotice" json:"PinnedNotice" toml:"PinnedNotice" yaml:"PinnedNotice"`
Pins PinSlice `boil:"Pins" json:"Pins" toml:"Pins" yaml:"Pins"`
}
// NewStruct creates a new relationship struct
@ -359,23 +359,31 @@ func (q noticeQuery) Exists(ctx context.Context, exec boil.ContextExecutor) (boo
return count > 0, nil
}
// PinnedNotice pointed to by the foreign key.
func (o *Notice) PinnedNotice(mods ...qm.QueryMod) pinnedNoticeQuery {
queryMods := []qm.QueryMod{
qm.Where("\"notice_id\" = ?", o.ID),
// Pins retrieves all the pin's Pins with an executor.
func (o *Notice) Pins(mods ...qm.QueryMod) pinQuery {
var queryMods []qm.QueryMod
if len(mods) != 0 {
queryMods = append(queryMods, mods...)
}
queryMods = append(queryMods, mods...)
queryMods = append(queryMods,
qm.InnerJoin("\"pin_notices\" on \"pins\".\"id\" = \"pin_notices\".\"pin_id\""),
qm.Where("\"pin_notices\".\"notice_id\"=?", o.ID),
)
query := PinnedNotices(queryMods...)
queries.SetFrom(query.Query, "\"pinned_notices\"")
query := Pins(queryMods...)
queries.SetFrom(query.Query, "\"pins\"")
if len(queries.GetSelect(query.Query)) == 0 {
queries.SetSelect(query.Query, []string{"\"pins\".*"})
}
return query
}
// LoadPinnedNotice allows an eager lookup of values, cached into the
// loaded structs of the objects. This is for a 1-1 relationship.
func (noticeL) LoadPinnedNotice(ctx context.Context, e boil.ContextExecutor, singular bool, maybeNotice interface{}, mods queries.Applicator) error {
// LoadPins allows an eager lookup of values, cached into the
// loaded structs of the objects. This is for a 1-M or N-M relationship.
func (noticeL) LoadPins(ctx context.Context, e boil.ContextExecutor, singular bool, maybeNotice interface{}, mods queries.Applicator) error {
var slice []*Notice
var object *Notice
@ -413,8 +421,10 @@ func (noticeL) LoadPinnedNotice(ctx context.Context, e boil.ContextExecutor, sin
}
query := NewQuery(
qm.From(`pinned_notices`),
qm.WhereIn(`pinned_notices.notice_id in ?`, args...),
qm.Select("\"pins\".id, \"pins\".name, \"a\".\"notice_id\""),
qm.From("\"pins\""),
qm.InnerJoin("\"pin_notices\" as \"a\" on \"pins\".\"id\" = \"a\".\"pin_id\""),
qm.WhereIn("\"a\".\"notice_id\" in ?", args...),
)
if mods != nil {
mods.Apply(query)
@ -422,50 +432,62 @@ func (noticeL) LoadPinnedNotice(ctx context.Context, e boil.ContextExecutor, sin
results, err := query.QueryContext(ctx, e)
if err != nil {
return errors.Wrap(err, "failed to eager load PinnedNotice")
return errors.Wrap(err, "failed to eager load pins")
}
var resultSlice []*PinnedNotice
if err = queries.Bind(results, &resultSlice); err != nil {
return errors.Wrap(err, "failed to bind eager loaded slice PinnedNotice")
var resultSlice []*Pin
var localJoinCols []int64
for results.Next() {
one := new(Pin)
var localJoinCol int64
err = results.Scan(&one.ID, &one.Name, &localJoinCol)
if err != nil {
return errors.Wrap(err, "failed to scan eager loaded results for pins")
}
if err = results.Err(); err != nil {
return errors.Wrap(err, "failed to plebian-bind eager loaded slice pins")
}
resultSlice = append(resultSlice, one)
localJoinCols = append(localJoinCols, localJoinCol)
}
if err = results.Close(); err != nil {
return errors.Wrap(err, "failed to close results of eager load for pinned_notices")
return errors.Wrap(err, "failed to close results in eager load on pins")
}
if err = results.Err(); err != nil {
return errors.Wrap(err, "error occurred during iteration of eager loaded relations for pinned_notices")
return errors.Wrap(err, "error occurred during iteration of eager loaded relations for pins")
}
if len(noticeAfterSelectHooks) != 0 {
if len(pinAfterSelectHooks) != 0 {
for _, obj := range resultSlice {
if err := obj.doAfterSelectHooks(ctx, e); err != nil {
return err
}
}
}
if len(resultSlice) == 0 {
if singular {
object.R.Pins = resultSlice
for _, foreign := range resultSlice {
if foreign.R == nil {
foreign.R = &pinR{}
}
foreign.R.Notices = append(foreign.R.Notices, object)
}
return nil
}
if singular {
foreign := resultSlice[0]
object.R.PinnedNotice = foreign
if foreign.R == nil {
foreign.R = &pinnedNoticeR{}
}
foreign.R.Notice = object
}
for _, local := range slice {
for _, foreign := range resultSlice {
if local.ID == foreign.NoticeID {
local.R.PinnedNotice = foreign
for i, foreign := range resultSlice {
localJoinCol := localJoinCols[i]
for _, local := range slice {
if local.ID == localJoinCol {
local.R.Pins = append(local.R.Pins, foreign)
if foreign.R == nil {
foreign.R = &pinnedNoticeR{}
foreign.R = &pinR{}
}
foreign.R.Notice = local
foreign.R.Notices = append(foreign.R.Notices, local)
break
}
}
@ -474,57 +496,146 @@ func (noticeL) LoadPinnedNotice(ctx context.Context, e boil.ContextExecutor, sin
return nil
}
// SetPinnedNotice of the notice to the related item.
// Sets o.R.PinnedNotice to related.
// Adds o to related.R.Notice.
func (o *Notice) SetPinnedNotice(ctx context.Context, exec boil.ContextExecutor, insert bool, related *PinnedNotice) error {
// AddPins adds the given related objects to the existing relationships
// of the notice, optionally inserting them as new records.
// Appends related to o.R.Pins.
// Sets related.R.Notices appropriately.
func (o *Notice) AddPins(ctx context.Context, exec boil.ContextExecutor, insert bool, related ...*Pin) error {
var err error
if insert {
related.NoticeID = o.ID
if err = related.Insert(ctx, exec, boil.Infer()); err != nil {
return errors.Wrap(err, "failed to insert into foreign table")
for _, rel := range related {
if insert {
if err = rel.Insert(ctx, exec, boil.Infer()); err != nil {
return errors.Wrap(err, "failed to insert into foreign table")
}
}
} else {
updateQuery := fmt.Sprintf(
"UPDATE \"pinned_notices\" SET %s WHERE %s",
strmangle.SetParamNames("\"", "\"", 0, []string{"notice_id"}),
strmangle.WhereClause("\"", "\"", 0, pinnedNoticePrimaryKeyColumns),
)
values := []interface{}{o.ID, related.Name, related.Language}
}
for _, rel := range related {
query := "insert into \"pin_notices\" (\"notice_id\", \"pin_id\") values (?, ?)"
values := []interface{}{o.ID, rel.ID}
if boil.IsDebug(ctx) {
writer := boil.DebugWriterFrom(ctx)
fmt.Fprintln(writer, updateQuery)
fmt.Fprintln(writer, query)
fmt.Fprintln(writer, values)
}
if _, err = exec.ExecContext(ctx, updateQuery, values...); err != nil {
return errors.Wrap(err, "failed to update foreign table")
_, err = exec.ExecContext(ctx, query, values...)
if err != nil {
return errors.Wrap(err, "failed to insert into join table")
}
related.NoticeID = o.ID
}
if o.R == nil {
o.R = &noticeR{
PinnedNotice: related,
Pins: related,
}
} else {
o.R.PinnedNotice = related
o.R.Pins = append(o.R.Pins, related...)
}
if related.R == nil {
related.R = &pinnedNoticeR{
Notice: o,
for _, rel := range related {
if rel.R == nil {
rel.R = &pinR{
Notices: NoticeSlice{o},
}
} else {
rel.R.Notices = append(rel.R.Notices, o)
}
} else {
related.R.Notice = o
}
return nil
}
// SetPins removes all previously related items of the
// notice replacing them completely with the passed
// in related items, optionally inserting them as new records.
// Sets o.R.Notices's Pins accordingly.
// Replaces o.R.Pins with related.
// Sets related.R.Notices's Pins accordingly.
func (o *Notice) SetPins(ctx context.Context, exec boil.ContextExecutor, insert bool, related ...*Pin) error {
query := "delete from \"pin_notices\" where \"notice_id\" = ?"
values := []interface{}{o.ID}
if boil.IsDebug(ctx) {
writer := boil.DebugWriterFrom(ctx)
fmt.Fprintln(writer, query)
fmt.Fprintln(writer, values)
}
_, err := exec.ExecContext(ctx, query, values...)
if err != nil {
return errors.Wrap(err, "failed to remove relationships before set")
}
removePinsFromNoticesSlice(o, related)
if o.R != nil {
o.R.Pins = nil
}
return o.AddPins(ctx, exec, insert, related...)
}
// RemovePins relationships from objects passed in.
// Removes related items from R.Pins (uses pointer comparison, removal does not keep order)
// Sets related.R.Notices.
func (o *Notice) RemovePins(ctx context.Context, exec boil.ContextExecutor, related ...*Pin) error {
var err error
query := fmt.Sprintf(
"delete from \"pin_notices\" where \"notice_id\" = ? and \"pin_id\" in (%s)",
strmangle.Placeholders(dialect.UseIndexPlaceholders, len(related), 2, 1),
)
values := []interface{}{o.ID}
for _, rel := range related {
values = append(values, rel.ID)
}
if boil.IsDebug(ctx) {
writer := boil.DebugWriterFrom(ctx)
fmt.Fprintln(writer, query)
fmt.Fprintln(writer, values)
}
_, err = exec.ExecContext(ctx, query, values...)
if err != nil {
return errors.Wrap(err, "failed to remove relationships before set")
}
removePinsFromNoticesSlice(o, related)
if o.R == nil {
return nil
}
for _, rel := range related {
for i, ri := range o.R.Pins {
if rel != ri {
continue
}
ln := len(o.R.Pins)
if ln > 1 && i < ln-1 {
o.R.Pins[i] = o.R.Pins[ln-1]
}
o.R.Pins = o.R.Pins[:ln-1]
break
}
}
return nil
}
func removePinsFromNoticesSlice(o *Notice, related []*Pin) {
for _, rel := range related {
if rel.R == nil {
continue
}
for i, ri := range rel.R.Notices {
if o.ID != ri.ID {
continue
}
ln := len(rel.R.Notices)
if ln > 1 && i < ln-1 {
rel.R.Notices[i] = rel.R.Notices[ln-1]
}
rel.R.Notices = rel.R.Notices[:ln-1]
break
}
}
}
// Notices retrieves all the records using an executor.
func Notices(mods ...qm.QueryMod) noticeQuery {
mods = append(mods, qm.From("\"notices\""))

View File

@ -1,943 +0,0 @@
// Code generated by SQLBoiler 4.4.0 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT.
// This file is meant to be re-generated in place and/or deleted at any time.
package models
import (
"context"
"database/sql"
"fmt"
"reflect"
"strings"
"sync"
"time"
"github.com/friendsofgo/errors"
"github.com/volatiletech/sqlboiler/v4/boil"
"github.com/volatiletech/sqlboiler/v4/queries"
"github.com/volatiletech/sqlboiler/v4/queries/qm"
"github.com/volatiletech/sqlboiler/v4/queries/qmhelper"
"github.com/volatiletech/strmangle"
)
// PinnedNotice is an object representing the database table.
type PinnedNotice struct {
Name string `boil:"name" json:"name" toml:"name" yaml:"name"`
NoticeID int64 `boil:"notice_id" json:"notice_id" toml:"notice_id" yaml:"notice_id"`
Language string `boil:"language" json:"language" toml:"language" yaml:"language"`
R *pinnedNoticeR `boil:"-" json:"-" toml:"-" yaml:"-"`
L pinnedNoticeL `boil:"-" json:"-" toml:"-" yaml:"-"`
}
var PinnedNoticeColumns = struct {
Name string
NoticeID string
Language string
}{
Name: "name",
NoticeID: "notice_id",
Language: "language",
}
// Generated where
var PinnedNoticeWhere = struct {
Name whereHelperstring
NoticeID whereHelperint64
Language whereHelperstring
}{
Name: whereHelperstring{field: "\"pinned_notices\".\"name\""},
NoticeID: whereHelperint64{field: "\"pinned_notices\".\"notice_id\""},
Language: whereHelperstring{field: "\"pinned_notices\".\"language\""},
}
// PinnedNoticeRels is where relationship names are stored.
var PinnedNoticeRels = struct {
Notice string
}{
Notice: "Notice",
}
// pinnedNoticeR is where relationships are stored.
type pinnedNoticeR struct {
Notice *Notice `boil:"Notice" json:"Notice" toml:"Notice" yaml:"Notice"`
}
// NewStruct creates a new relationship struct
func (*pinnedNoticeR) NewStruct() *pinnedNoticeR {
return &pinnedNoticeR{}
}
// pinnedNoticeL is where Load methods for each relationship are stored.
type pinnedNoticeL struct{}
var (
pinnedNoticeAllColumns = []string{"name", "notice_id", "language"}
pinnedNoticeColumnsWithoutDefault = []string{"name", "notice_id", "language"}
pinnedNoticeColumnsWithDefault = []string{}
pinnedNoticePrimaryKeyColumns = []string{"name", "language"}
)
type (
// PinnedNoticeSlice is an alias for a slice of pointers to PinnedNotice.
// This should generally be used opposed to []PinnedNotice.
PinnedNoticeSlice []*PinnedNotice
// PinnedNoticeHook is the signature for custom PinnedNotice hook methods
PinnedNoticeHook func(context.Context, boil.ContextExecutor, *PinnedNotice) error
pinnedNoticeQuery struct {
*queries.Query
}
)
// Cache for insert, update and upsert
var (
pinnedNoticeType = reflect.TypeOf(&PinnedNotice{})
pinnedNoticeMapping = queries.MakeStructMapping(pinnedNoticeType)
pinnedNoticePrimaryKeyMapping, _ = queries.BindMapping(pinnedNoticeType, pinnedNoticeMapping, pinnedNoticePrimaryKeyColumns)
pinnedNoticeInsertCacheMut sync.RWMutex
pinnedNoticeInsertCache = make(map[string]insertCache)
pinnedNoticeUpdateCacheMut sync.RWMutex
pinnedNoticeUpdateCache = make(map[string]updateCache)
pinnedNoticeUpsertCacheMut sync.RWMutex
pinnedNoticeUpsertCache = make(map[string]insertCache)
)
var (
// Force time package dependency for automated UpdatedAt/CreatedAt.
_ = time.Second
// Force qmhelper dependency for where clause generation (which doesn't
// always happen)
_ = qmhelper.Where
)
var pinnedNoticeBeforeInsertHooks []PinnedNoticeHook
var pinnedNoticeBeforeUpdateHooks []PinnedNoticeHook
var pinnedNoticeBeforeDeleteHooks []PinnedNoticeHook
var pinnedNoticeBeforeUpsertHooks []PinnedNoticeHook
var pinnedNoticeAfterInsertHooks []PinnedNoticeHook
var pinnedNoticeAfterSelectHooks []PinnedNoticeHook
var pinnedNoticeAfterUpdateHooks []PinnedNoticeHook
var pinnedNoticeAfterDeleteHooks []PinnedNoticeHook
var pinnedNoticeAfterUpsertHooks []PinnedNoticeHook
// doBeforeInsertHooks executes all "before insert" hooks.
func (o *PinnedNotice) doBeforeInsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range pinnedNoticeBeforeInsertHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doBeforeUpdateHooks executes all "before Update" hooks.
func (o *PinnedNotice) doBeforeUpdateHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range pinnedNoticeBeforeUpdateHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doBeforeDeleteHooks executes all "before Delete" hooks.
func (o *PinnedNotice) doBeforeDeleteHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range pinnedNoticeBeforeDeleteHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doBeforeUpsertHooks executes all "before Upsert" hooks.
func (o *PinnedNotice) doBeforeUpsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range pinnedNoticeBeforeUpsertHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doAfterInsertHooks executes all "after Insert" hooks.
func (o *PinnedNotice) doAfterInsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range pinnedNoticeAfterInsertHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doAfterSelectHooks executes all "after Select" hooks.
func (o *PinnedNotice) doAfterSelectHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range pinnedNoticeAfterSelectHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doAfterUpdateHooks executes all "after Update" hooks.
func (o *PinnedNotice) doAfterUpdateHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range pinnedNoticeAfterUpdateHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doAfterDeleteHooks executes all "after Delete" hooks.
func (o *PinnedNotice) doAfterDeleteHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range pinnedNoticeAfterDeleteHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// doAfterUpsertHooks executes all "after Upsert" hooks.
func (o *PinnedNotice) doAfterUpsertHooks(ctx context.Context, exec boil.ContextExecutor) (err error) {
if boil.HooksAreSkipped(ctx) {
return nil
}
for _, hook := range pinnedNoticeAfterUpsertHooks {
if err := hook(ctx, exec, o); err != nil {
return err
}
}
return nil
}
// AddPinnedNoticeHook registers your hook function for all future operations.
func AddPinnedNoticeHook(hookPoint boil.HookPoint, pinnedNoticeHook PinnedNoticeHook) {
switch hookPoint {
case boil.BeforeInsertHook:
pinnedNoticeBeforeInsertHooks = append(pinnedNoticeBeforeInsertHooks, pinnedNoticeHook)
case boil.BeforeUpdateHook:
pinnedNoticeBeforeUpdateHooks = append(pinnedNoticeBeforeUpdateHooks, pinnedNoticeHook)
case boil.BeforeDeleteHook:
pinnedNoticeBeforeDeleteHooks = append(pinnedNoticeBeforeDeleteHooks, pinnedNoticeHook)
case boil.BeforeUpsertHook:
pinnedNoticeBeforeUpsertHooks = append(pinnedNoticeBeforeUpsertHooks, pinnedNoticeHook)
case boil.AfterInsertHook:
pinnedNoticeAfterInsertHooks = append(pinnedNoticeAfterInsertHooks, pinnedNoticeHook)
case boil.AfterSelectHook:
pinnedNoticeAfterSelectHooks = append(pinnedNoticeAfterSelectHooks, pinnedNoticeHook)
case boil.AfterUpdateHook:
pinnedNoticeAfterUpdateHooks = append(pinnedNoticeAfterUpdateHooks, pinnedNoticeHook)
case boil.AfterDeleteHook:
pinnedNoticeAfterDeleteHooks = append(pinnedNoticeAfterDeleteHooks, pinnedNoticeHook)
case boil.AfterUpsertHook:
pinnedNoticeAfterUpsertHooks = append(pinnedNoticeAfterUpsertHooks, pinnedNoticeHook)
}
}
// One returns a single pinnedNotice record from the query.
func (q pinnedNoticeQuery) One(ctx context.Context, exec boil.ContextExecutor) (*PinnedNotice, error) {
o := &PinnedNotice{}
queries.SetLimit(q.Query, 1)
err := q.Bind(ctx, exec, o)
if err != nil {
if errors.Cause(err) == sql.ErrNoRows {
return nil, sql.ErrNoRows
}
return nil, errors.Wrap(err, "models: failed to execute a one query for pinned_notices")
}
if err := o.doAfterSelectHooks(ctx, exec); err != nil {
return o, err
}
return o, nil
}
// All returns all PinnedNotice records from the query.
func (q pinnedNoticeQuery) All(ctx context.Context, exec boil.ContextExecutor) (PinnedNoticeSlice, error) {
var o []*PinnedNotice
err := q.Bind(ctx, exec, &o)
if err != nil {
return nil, errors.Wrap(err, "models: failed to assign all query results to PinnedNotice slice")
}
if len(pinnedNoticeAfterSelectHooks) != 0 {
for _, obj := range o {
if err := obj.doAfterSelectHooks(ctx, exec); err != nil {
return o, err
}
}
}
return o, nil
}
// Count returns the count of all PinnedNotice records in the query.
func (q pinnedNoticeQuery) Count(ctx context.Context, exec boil.ContextExecutor) (int64, error) {
var count int64
queries.SetSelect(q.Query, nil)
queries.SetCount(q.Query)
err := q.Query.QueryRowContext(ctx, exec).Scan(&count)
if err != nil {
return 0, errors.Wrap(err, "models: failed to count pinned_notices rows")
}
return count, nil
}
// Exists checks if the row exists in the table.
func (q pinnedNoticeQuery) Exists(ctx context.Context, exec boil.ContextExecutor) (bool, error) {
var count int64
queries.SetSelect(q.Query, nil)
queries.SetCount(q.Query)
queries.SetLimit(q.Query, 1)
err := q.Query.QueryRowContext(ctx, exec).Scan(&count)
if err != nil {
return false, errors.Wrap(err, "models: failed to check if pinned_notices exists")
}
return count > 0, nil
}
// Notice pointed to by the foreign key.
func (o *PinnedNotice) Notice(mods ...qm.QueryMod) noticeQuery {
queryMods := []qm.QueryMod{
qm.Where("\"id\" = ?", o.NoticeID),
}
queryMods = append(queryMods, mods...)
query := Notices(queryMods...)
queries.SetFrom(query.Query, "\"notices\"")
return query
}
// LoadNotice allows an eager lookup of values, cached into the
// loaded structs of the objects. This is for an N-1 relationship.
func (pinnedNoticeL) LoadNotice(ctx context.Context, e boil.ContextExecutor, singular bool, maybePinnedNotice interface{}, mods queries.Applicator) error {
var slice []*PinnedNotice
var object *PinnedNotice
if singular {
object = maybePinnedNotice.(*PinnedNotice)
} else {
slice = *maybePinnedNotice.(*[]*PinnedNotice)
}
args := make([]interface{}, 0, 1)
if singular {
if object.R == nil {
object.R = &pinnedNoticeR{}
}
args = append(args, object.NoticeID)
} else {
Outer:
for _, obj := range slice {
if obj.R == nil {
obj.R = &pinnedNoticeR{}
}
for _, a := range args {
if a == obj.NoticeID {
continue Outer
}
}
args = append(args, obj.NoticeID)
}
}
if len(args) == 0 {
return nil
}
query := NewQuery(
qm.From(`notices`),
qm.WhereIn(`notices.id in ?`, args...),
)
if mods != nil {
mods.Apply(query)
}
results, err := query.QueryContext(ctx, e)
if err != nil {
return errors.Wrap(err, "failed to eager load Notice")
}
var resultSlice []*Notice
if err = queries.Bind(results, &resultSlice); err != nil {
return errors.Wrap(err, "failed to bind eager loaded slice Notice")
}
if err = results.Close(); err != nil {
return errors.Wrap(err, "failed to close results of eager load for notices")
}
if err = results.Err(); err != nil {
return errors.Wrap(err, "error occurred during iteration of eager loaded relations for notices")
}
if len(pinnedNoticeAfterSelectHooks) != 0 {
for _, obj := range resultSlice {
if err := obj.doAfterSelectHooks(ctx, e); err != nil {
return err
}
}
}
if len(resultSlice) == 0 {
return nil
}
if singular {
foreign := resultSlice[0]
object.R.Notice = foreign
if foreign.R == nil {
foreign.R = &noticeR{}
}
foreign.R.PinnedNotice = object
return nil
}
for _, local := range slice {
for _, foreign := range resultSlice {
if local.NoticeID == foreign.ID {
local.R.Notice = foreign
if foreign.R == nil {
foreign.R = &noticeR{}
}
foreign.R.PinnedNotice = local
break
}
}
}
return nil
}
// SetNotice of the pinnedNotice to the related item.
// Sets o.R.Notice to related.
// Adds o to related.R.PinnedNotice.
func (o *PinnedNotice) SetNotice(ctx context.Context, exec boil.ContextExecutor, insert bool, related *Notice) error {
var err error
if insert {
if err = related.Insert(ctx, exec, boil.Infer()); err != nil {
return errors.Wrap(err, "failed to insert into foreign table")
}
}
updateQuery := fmt.Sprintf(
"UPDATE \"pinned_notices\" SET %s WHERE %s",
strmangle.SetParamNames("\"", "\"", 0, []string{"notice_id"}),
strmangle.WhereClause("\"", "\"", 0, pinnedNoticePrimaryKeyColumns),
)
values := []interface{}{related.ID, o.Name, o.Language}
if boil.IsDebug(ctx) {
writer := boil.DebugWriterFrom(ctx)
fmt.Fprintln(writer, updateQuery)
fmt.Fprintln(writer, values)
}
if _, err = exec.ExecContext(ctx, updateQuery, values...); err != nil {
return errors.Wrap(err, "failed to update local table")
}
o.NoticeID = related.ID
if o.R == nil {
o.R = &pinnedNoticeR{
Notice: related,
}
} else {
o.R.Notice = related
}
if related.R == nil {
related.R = &noticeR{
PinnedNotice: o,
}
} else {
related.R.PinnedNotice = o
}
return nil
}
// PinnedNotices retrieves all the records using an executor.
func PinnedNotices(mods ...qm.QueryMod) pinnedNoticeQuery {
mods = append(mods, qm.From("\"pinned_notices\""))
return pinnedNoticeQuery{NewQuery(mods...)}
}
// FindPinnedNotice retrieves a single record by ID with an executor.
// If selectCols is empty Find will return all columns.
func FindPinnedNotice(ctx context.Context, exec boil.ContextExecutor, name string, language string, selectCols ...string) (*PinnedNotice, error) {
pinnedNoticeObj := &PinnedNotice{}
sel := "*"
if len(selectCols) > 0 {
sel = strings.Join(strmangle.IdentQuoteSlice(dialect.LQ, dialect.RQ, selectCols), ",")
}
query := fmt.Sprintf(
"select %s from \"pinned_notices\" where \"name\"=? AND \"language\"=?", sel,
)
q := queries.Raw(query, name, language)
err := q.Bind(ctx, exec, pinnedNoticeObj)
if err != nil {
if errors.Cause(err) == sql.ErrNoRows {
return nil, sql.ErrNoRows
}
return nil, errors.Wrap(err, "models: unable to select from pinned_notices")
}
return pinnedNoticeObj, nil
}
// Insert a single record using an executor.
// See boil.Columns.InsertColumnSet documentation to understand column list inference for inserts.
func (o *PinnedNotice) Insert(ctx context.Context, exec boil.ContextExecutor, columns boil.Columns) error {
if o == nil {
return errors.New("models: no pinned_notices provided for insertion")
}
var err error
if err := o.doBeforeInsertHooks(ctx, exec); err != nil {
return err
}
nzDefaults := queries.NonZeroDefaultSet(pinnedNoticeColumnsWithDefault, o)
key := makeCacheKey(columns, nzDefaults)
pinnedNoticeInsertCacheMut.RLock()
cache, cached := pinnedNoticeInsertCache[key]
pinnedNoticeInsertCacheMut.RUnlock()
if !cached {
wl, returnColumns := columns.InsertColumnSet(
pinnedNoticeAllColumns,
pinnedNoticeColumnsWithDefault,
pinnedNoticeColumnsWithoutDefault,
nzDefaults,
)
cache.valueMapping, err = queries.BindMapping(pinnedNoticeType, pinnedNoticeMapping, wl)
if err != nil {
return err
}
cache.retMapping, err = queries.BindMapping(pinnedNoticeType, pinnedNoticeMapping, returnColumns)
if err != nil {
return err
}
if len(wl) != 0 {
cache.query = fmt.Sprintf("INSERT INTO \"pinned_notices\" (\"%s\") %%sVALUES (%s)%%s", strings.Join(wl, "\",\""), strmangle.Placeholders(dialect.UseIndexPlaceholders, len(wl), 1, 1))
} else {
cache.query = "INSERT INTO \"pinned_notices\" %sDEFAULT VALUES%s"
}
var queryOutput, queryReturning string
if len(cache.retMapping) != 0 {
cache.retQuery = fmt.Sprintf("SELECT \"%s\" FROM \"pinned_notices\" WHERE %s", strings.Join(returnColumns, "\",\""), strmangle.WhereClause("\"", "\"", 0, pinnedNoticePrimaryKeyColumns))
}
cache.query = fmt.Sprintf(cache.query, queryOutput, queryReturning)
}
value := reflect.Indirect(reflect.ValueOf(o))
vals := queries.ValuesFromMapping(value, cache.valueMapping)
if boil.IsDebug(ctx) {
writer := boil.DebugWriterFrom(ctx)
fmt.Fprintln(writer, cache.query)
fmt.Fprintln(writer, vals)
}
_, err = exec.ExecContext(ctx, cache.query, vals...)
if err != nil {
return errors.Wrap(err, "models: unable to insert into pinned_notices")
}
var identifierCols []interface{}
if len(cache.retMapping) == 0 {
goto CacheNoHooks
}
identifierCols = []interface{}{
o.Name,
o.Language,
}
if boil.IsDebug(ctx) {
writer := boil.DebugWriterFrom(ctx)
fmt.Fprintln(writer, cache.retQuery)
fmt.Fprintln(writer, identifierCols...)
}
err = exec.QueryRowContext(ctx, cache.retQuery, identifierCols...).Scan(queries.PtrsFromMapping(value, cache.retMapping)...)
if err != nil {
return errors.Wrap(err, "models: unable to populate default values for pinned_notices")
}
CacheNoHooks:
if !cached {
pinnedNoticeInsertCacheMut.Lock()
pinnedNoticeInsertCache[key] = cache
pinnedNoticeInsertCacheMut.Unlock()
}
return o.doAfterInsertHooks(ctx, exec)
}
// Update uses an executor to update the PinnedNotice.
// See boil.Columns.UpdateColumnSet documentation to understand column list inference for updates.
// Update does not automatically update the record in case of default values. Use .Reload() to refresh the records.
func (o *PinnedNotice) Update(ctx context.Context, exec boil.ContextExecutor, columns boil.Columns) (int64, error) {
var err error
if err = o.doBeforeUpdateHooks(ctx, exec); err != nil {
return 0, err
}
key := makeCacheKey(columns, nil)
pinnedNoticeUpdateCacheMut.RLock()
cache, cached := pinnedNoticeUpdateCache[key]
pinnedNoticeUpdateCacheMut.RUnlock()
if !cached {
wl := columns.UpdateColumnSet(
pinnedNoticeAllColumns,
pinnedNoticePrimaryKeyColumns,
)
if !columns.IsWhitelist() {
wl = strmangle.SetComplement(wl, []string{"created_at"})
}
if len(wl) == 0 {
return 0, errors.New("models: unable to update pinned_notices, could not build whitelist")
}
cache.query = fmt.Sprintf("UPDATE \"pinned_notices\" SET %s WHERE %s",
strmangle.SetParamNames("\"", "\"", 0, wl),
strmangle.WhereClause("\"", "\"", 0, pinnedNoticePrimaryKeyColumns),
)
cache.valueMapping, err = queries.BindMapping(pinnedNoticeType, pinnedNoticeMapping, append(wl, pinnedNoticePrimaryKeyColumns...))
if err != nil {
return 0, err
}
}
values := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(o)), cache.valueMapping)
if boil.IsDebug(ctx) {
writer := boil.DebugWriterFrom(ctx)
fmt.Fprintln(writer, cache.query)
fmt.Fprintln(writer, values)
}
var result sql.Result
result, err = exec.ExecContext(ctx, cache.query, values...)
if err != nil {
return 0, errors.Wrap(err, "models: unable to update pinned_notices row")
}
rowsAff, err := result.RowsAffected()
if err != nil {
return 0, errors.Wrap(err, "models: failed to get rows affected by update for pinned_notices")
}
if !cached {
pinnedNoticeUpdateCacheMut.Lock()
pinnedNoticeUpdateCache[key] = cache
pinnedNoticeUpdateCacheMut.Unlock()
}
return rowsAff, o.doAfterUpdateHooks(ctx, exec)
}
// UpdateAll updates all rows with the specified column values.
func (q pinnedNoticeQuery) UpdateAll(ctx context.Context, exec boil.ContextExecutor, cols M) (int64, error) {
queries.SetUpdate(q.Query, cols)
result, err := q.Query.ExecContext(ctx, exec)
if err != nil {
return 0, errors.Wrap(err, "models: unable to update all for pinned_notices")
}
rowsAff, err := result.RowsAffected()
if err != nil {
return 0, errors.Wrap(err, "models: unable to retrieve rows affected for pinned_notices")
}
return rowsAff, nil
}
// UpdateAll updates all rows with the specified column values, using an executor.
func (o PinnedNoticeSlice) UpdateAll(ctx context.Context, exec boil.ContextExecutor, cols M) (int64, error) {
ln := int64(len(o))
if ln == 0 {
return 0, nil
}
if len(cols) == 0 {
return 0, errors.New("models: update all requires at least one column argument")
}
colNames := make([]string, len(cols))
args := make([]interface{}, len(cols))
i := 0
for name, value := range cols {
colNames[i] = name
args[i] = value
i++
}
// Append all of the primary key values for each column
for _, obj := range o {
pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), pinnedNoticePrimaryKeyMapping)
args = append(args, pkeyArgs...)
}
sql := fmt.Sprintf("UPDATE \"pinned_notices\" SET %s WHERE %s",
strmangle.SetParamNames("\"", "\"", 0, colNames),
strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 0, pinnedNoticePrimaryKeyColumns, len(o)))
if boil.IsDebug(ctx) {
writer := boil.DebugWriterFrom(ctx)
fmt.Fprintln(writer, sql)
fmt.Fprintln(writer, args...)
}
result, err := exec.ExecContext(ctx, sql, args...)
if err != nil {
return 0, errors.Wrap(err, "models: unable to update all in pinnedNotice slice")
}
rowsAff, err := result.RowsAffected()
if err != nil {
return 0, errors.Wrap(err, "models: unable to retrieve rows affected all in update all pinnedNotice")
}
return rowsAff, nil
}
// Delete deletes a single PinnedNotice record with an executor.
// Delete will match against the primary key column to find the record to delete.
func (o *PinnedNotice) Delete(ctx context.Context, exec boil.ContextExecutor) (int64, error) {
if o == nil {
return 0, errors.New("models: no PinnedNotice provided for delete")
}
if err := o.doBeforeDeleteHooks(ctx, exec); err != nil {
return 0, err
}
args := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(o)), pinnedNoticePrimaryKeyMapping)
sql := "DELETE FROM \"pinned_notices\" WHERE \"name\"=? AND \"language\"=?"
if boil.IsDebug(ctx) {
writer := boil.DebugWriterFrom(ctx)
fmt.Fprintln(writer, sql)
fmt.Fprintln(writer, args...)
}
result, err := exec.ExecContext(ctx, sql, args...)
if err != nil {
return 0, errors.Wrap(err, "models: unable to delete from pinned_notices")
}
rowsAff, err := result.RowsAffected()
if err != nil {
return 0, errors.Wrap(err, "models: failed to get rows affected by delete for pinned_notices")
}
if err := o.doAfterDeleteHooks(ctx, exec); err != nil {
return 0, err
}
return rowsAff, nil
}
// DeleteAll deletes all matching rows.
func (q pinnedNoticeQuery) DeleteAll(ctx context.Context, exec boil.ContextExecutor) (int64, error) {
if q.Query == nil {
return 0, errors.New("models: no pinnedNoticeQuery provided for delete all")
}
queries.SetDelete(q.Query)
result, err := q.Query.ExecContext(ctx, exec)
if err != nil {
return 0, errors.Wrap(err, "models: unable to delete all from pinned_notices")
}
rowsAff, err := result.RowsAffected()
if err != nil {
return 0, errors.Wrap(err, "models: failed to get rows affected by deleteall for pinned_notices")
}
return rowsAff, nil
}
// DeleteAll deletes all rows in the slice, using an executor.
func (o PinnedNoticeSlice) DeleteAll(ctx context.Context, exec boil.ContextExecutor) (int64, error) {
if len(o) == 0 {
return 0, nil
}
if len(pinnedNoticeBeforeDeleteHooks) != 0 {
for _, obj := range o {
if err := obj.doBeforeDeleteHooks(ctx, exec); err != nil {
return 0, err
}
}
}
var args []interface{}
for _, obj := range o {
pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), pinnedNoticePrimaryKeyMapping)
args = append(args, pkeyArgs...)
}
sql := "DELETE FROM \"pinned_notices\" WHERE " +
strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 0, pinnedNoticePrimaryKeyColumns, len(o))
if boil.IsDebug(ctx) {
writer := boil.DebugWriterFrom(ctx)
fmt.Fprintln(writer, sql)
fmt.Fprintln(writer, args)
}
result, err := exec.ExecContext(ctx, sql, args...)
if err != nil {
return 0, errors.Wrap(err, "models: unable to delete all from pinnedNotice slice")
}
rowsAff, err := result.RowsAffected()
if err != nil {
return 0, errors.Wrap(err, "models: failed to get rows affected by deleteall for pinned_notices")
}
if len(pinnedNoticeAfterDeleteHooks) != 0 {
for _, obj := range o {
if err := obj.doAfterDeleteHooks(ctx, exec); err != nil {
return 0, err
}
}
}
return rowsAff, nil
}
// Reload refetches the object from the database
// using the primary keys with an executor.
func (o *PinnedNotice) Reload(ctx context.Context, exec boil.ContextExecutor) error {
ret, err := FindPinnedNotice(ctx, exec, o.Name, o.Language)
if err != nil {
return err
}
*o = *ret
return nil
}
// ReloadAll refetches every row with matching primary key column values
// and overwrites the original object slice with the newly updated slice.
func (o *PinnedNoticeSlice) ReloadAll(ctx context.Context, exec boil.ContextExecutor) error {
if o == nil || len(*o) == 0 {
return nil
}
slice := PinnedNoticeSlice{}
var args []interface{}
for _, obj := range *o {
pkeyArgs := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(obj)), pinnedNoticePrimaryKeyMapping)
args = append(args, pkeyArgs...)
}
sql := "SELECT \"pinned_notices\".* FROM \"pinned_notices\" WHERE " +
strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 0, pinnedNoticePrimaryKeyColumns, len(*o))
q := queries.Raw(sql, args...)
err := q.Bind(ctx, exec, &slice)
if err != nil {
return errors.Wrap(err, "models: unable to reload all in PinnedNoticeSlice")
}
*o = slice
return nil
}
// PinnedNoticeExists checks if the PinnedNotice row exists.
func PinnedNoticeExists(ctx context.Context, exec boil.ContextExecutor, name string, language string) (bool, error) {
var exists bool
sql := "select exists(select 1 from \"pinned_notices\" where \"name\"=? AND \"language\"=? limit 1)"
if boil.IsDebug(ctx) {
writer := boil.DebugWriterFrom(ctx)
fmt.Fprintln(writer, sql)
fmt.Fprintln(writer, name, language)
}
row := exec.QueryRowContext(ctx, sql, name, language)
err := row.Scan(&exists)
if err != nil {
return false, errors.Wrap(err, "models: unable to check if pinned_notices exists")
}
return exists, nil
}

File diff suppressed because it is too large Load Diff

View File

@ -5,9 +5,10 @@ import (
"database/sql"
"fmt"
"github.com/volatiletech/sqlboiler/v4/boil"
"github.com/friendsofgo/errors"
"github.com/volatiletech/sqlboiler/v4/boil"
"github.com/volatiletech/sqlboiler/v4/queries/qm"
"github.com/ssb-ngi-pointer/go-ssb-room/admindb"
"github.com/ssb-ngi-pointer/go-ssb-room/admindb/sqlite/models"
)
@ -19,12 +20,96 @@ type PinnedNotices struct {
db *sql.DB
}
func (pndb PinnedNotices) Set(name admindb.PinnedNoticeName, pageID int64, lang string) error {
if !name.Valid() {
return fmt.Errorf("fixed pages: invalid page name: %s", name)
// List returns a sortable map of all the pinned notices
func (pn PinnedNotices) List(ctx context.Context) (admindb.PinnedNotices, error) {
// get all the pins and eager-load the related notices
lst, err := models.Pins(qm.Load("Notices")).All(ctx, pn.db)
if err != nil {
return nil, err
}
return fmt.Errorf("TODO: set fixed page %s to %d:%s", name, pageID, lang)
// prepare a map for them
var pinnedMap = make(admindb.PinnedNotices, len(lst))
for _, entry := range lst {
// skip a pin if it has no entries
noticeCount := len(entry.R.Notices)
if noticeCount == 0 {
continue
}
name := admindb.PinnedNoticeName(entry.Name)
// get the exisint map entry or create a new slice
notices, has := pinnedMap[name]
if !has {
notices = make([]admindb.Notice, 0, noticeCount)
}
// add all the related notice to the slice
for _, n := range entry.R.Notices {
relatedNotice := admindb.Notice{
ID: n.ID,
Title: n.Title,
Content: n.Content,
Language: n.Language,
}
notices = append(notices, relatedNotice)
}
// update the map
pinnedMap[name] = notices
}
return pinnedMap, nil
}
func (pn PinnedNotices) Get(ctx context.Context, name admindb.PinnedNoticeName, lang string) (*admindb.Notice, error) {
p, err := models.Pins(
qm.Where("name = ?", name),
qm.Load("Notices", qm.Where("language = ?", lang)),
).One(ctx, pn.db)
if err != nil {
return nil, err
}
if n := len(p.R.Notices); n != 1 {
return nil, fmt.Errorf("pinnedNotice: expected 1 notice but got %d", n)
}
modelNotice := p.R.Notices[0]
return &admindb.Notice{
ID: modelNotice.ID,
Title: modelNotice.Title,
Content: modelNotice.Content,
Language: modelNotice.Language,
}, nil
}
func (pn PinnedNotices) Set(ctx context.Context, name admindb.PinnedNoticeName, noticeID int64) error {
if !name.Valid() {
return fmt.Errorf("pinned notice: invalid notice name: %s", name)
}
n, err := models.FindNotice(ctx, pn.db, noticeID)
if err != nil {
return err
}
p, err := models.Pins(qm.Where("name = ?", name)).One(ctx, pn.db)
if err != nil {
return err
}
err = p.AddNotices(ctx, pn.db, false, n)
if err != nil {
return err
}
return nil
}
// make sure to implement interfaces correctly
@ -34,28 +119,28 @@ type Notices struct {
db *sql.DB
}
func (ndb Notices) GetByID(ctx context.Context, id int64) (admindb.Notice, error) {
var n admindb.Notice
func (n Notices) GetByID(ctx context.Context, id int64) (admindb.Notice, error) {
var notice admindb.Notice
dbEntry, err := models.FindNotice(ctx, ndb.db, id)
dbEntry, err := models.FindNotice(ctx, n.db, id)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return n, admindb.ErrNotFound
return notice, admindb.ErrNotFound
}
return n, err
return notice, err
}
// convert models type to admindb type
n.ID = dbEntry.ID
n.Title = dbEntry.Title
n.Language = dbEntry.Language
n.Content = dbEntry.Content
notice.ID = dbEntry.ID
notice.Title = dbEntry.Title
notice.Language = dbEntry.Language
notice.Content = dbEntry.Content
return n, nil
return notice, nil
}
func (ndb Notices) RemoveID(ctx context.Context, id int64) error {
dbEntry, err := models.FindNotice(ctx, ndb.db, id)
func (n Notices) RemoveID(ctx context.Context, id int64) error {
dbEntry, err := models.FindNotice(ctx, n.db, id)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return admindb.ErrNotFound
@ -63,7 +148,7 @@ func (ndb Notices) RemoveID(ctx context.Context, id int64) error {
return err
}
_, err = dbEntry.Delete(ctx, ndb.db)
_, err = dbEntry.Delete(ctx, n.db)
if err != nil {
return err
}
@ -71,13 +156,13 @@ func (ndb Notices) RemoveID(ctx context.Context, id int64) error {
return nil
}
func (ndb Notices) Save(ctx context.Context, p *admindb.Notice) error {
func (n Notices) Save(ctx context.Context, p *admindb.Notice) error {
if p.ID == 0 {
var newEntry models.Notice
newEntry.Title = p.Title
newEntry.Content = p.Content
newEntry.Language = p.Language
err := newEntry.Insert(ctx, ndb.db, boil.Whitelist("title", "content", "language"))
err := newEntry.Insert(ctx, n.db, boil.Whitelist("title", "content", "language"))
if err != nil {
return err
}
@ -90,7 +175,7 @@ func (ndb Notices) Save(ctx context.Context, p *admindb.Notice) error {
existing.Title = p.Title
existing.Content = p.Content
existing.Language = p.Language
_, err := existing.Update(ctx, ndb.db, boil.Whitelist("title", "content", "language"))
_, err := existing.Update(ctx, n.db, boil.Whitelist("title", "content", "language"))
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return admindb.ErrNotFound

View File

@ -73,3 +73,93 @@ func TestNoticesCRUD(t *testing.T) {
r.EqualError(err, admindb.ErrNotFound.Error())
})
}
func TestPinnedNotices(t *testing.T) {
r := require.New(t)
ctx := context.Background()
testRepo := filepath.Join("testrun", t.Name())
os.RemoveAll(testRepo)
tr := repo.New(testRepo)
db, err := Open(tr)
r.NoError(err)
t.Run("defaults", func(t *testing.T) {
allTheNotices, err := db.PinnedNotices.List(ctx)
r.NoError(err)
type expectedNotices struct {
Name admindb.PinnedNoticeName
Count int
}
cases := []expectedNotices{
{admindb.NoticeDescription, 2},
{admindb.NoticeNews, 1},
{admindb.NoticePrivacyPolicy, 2},
{admindb.NoticeCodeOfConduct, 1},
}
for i, tcase := range cases {
desc, has := allTheNotices[tcase.Name]
r.True(has, "case %d failed - notice %s not in map", i, tcase.Name)
r.Len(desc, tcase.Count, "case %d failed - wrong number of notices for %s", i, tcase.Name)
}
})
t.Run("validity", func(t *testing.T) {
var empty admindb.Notice
// no id
err = db.PinnedNotices.Set(ctx, admindb.NoticeNews, empty.ID)
r.Error(err)
// not-null id
empty.ID = 999
err = db.PinnedNotices.Set(ctx, admindb.NoticeNews, empty.ID)
r.Error(err)
// invalid notice name
err = db.PinnedNotices.Set(ctx, "unknown", empty.ID)
r.Error(err)
})
t.Run("add new localization", func(t *testing.T) {
var notice admindb.Notice
notice.Title = "política de privacidad"
notice.Content = "solo una prueba"
notice.Language = "es"
// save the new notice
err = db.Notices.Save(ctx, &notice)
r.NoError(err)
// set it
err = db.PinnedNotices.Set(ctx, admindb.NoticePrivacyPolicy, notice.ID)
r.NoError(err)
// retreive it
ret, err := db.PinnedNotices.Get(ctx, admindb.NoticePrivacyPolicy, notice.Language)
r.NoError(err)
r.Equal(notice, *ret, "notices are not the same")
// see that it's in the list
allTheNotices, err := db.PinnedNotices.List(ctx)
r.NoError(err)
notices, has := allTheNotices[admindb.NoticePrivacyPolicy]
r.True(has)
r.Len(notices, 3)
has = false
for _, n := range notices {
if n.Title == notice.Title {
has = true
break
}
}
r.True(has, "did not find new notice in list()")
})
}

View File

@ -6,6 +6,7 @@ import (
"database/sql/driver"
"errors"
"fmt"
"sort"
refs "go.mindeco.de/ssb-refs"
)
@ -83,6 +84,8 @@ func (fpn PinnedNoticeName) Valid() bool {
fpn == NoticeCodeOfConduct
}
type PinnedNotices map[PinnedNoticeName][]Notice
// Notice holds the title and content of a page that is user generated
type Notice struct {
ID int64
@ -90,3 +93,38 @@ type Notice struct {
Content string
Language string
}
type PinnedNotice struct {
Name PinnedNoticeName
Notices []Notice
}
type SortedPinnedNotices []PinnedNotice
// Sorted returns a sorted list of the map, by the key names
func (pn PinnedNotices) Sorted() SortedPinnedNotices {
lst := make(SortedPinnedNotices, 0, len(pn))
for name, notices := range pn {
lst = append(lst, PinnedNotice{
Name: name,
Notices: notices,
})
}
sort.Sort(lst)
return lst
}
var _ sort.Interface = (SortedPinnedNotices)(nil)
func (byName SortedPinnedNotices) Len() int { return len(byName) }
func (byName SortedPinnedNotices) Less(i, j int) bool {
return byName[i].Name < byName[j].Name
}
func (byName SortedPinnedNotices) Swap(i, j int) {
byName[i], byName[j] = byName[j], byName[i]
}

View File

@ -218,6 +218,7 @@ func runroomsrv() error {
db.AuthFallback,
db.AllowList,
db.Notices,
db.PinnedNotices,
)
if err != nil {
return fmt.Errorf("failed to create HTTPdashboard handler: %w", err)

View File

@ -28,7 +28,8 @@ import (
var HTMLTempaltes = []string{
"landing/index.tmpl",
"landing/about.tmpl",
"notice.tmpl",
"notice/list.tmpl",
"notice/show.tmpl",
"error.tmpl",
}
@ -41,6 +42,7 @@ func New(
fs admindb.AuthFallbackService,
al admindb.AllowListService,
ns admindb.NoticesService,
ps admindb.PinnedNoticesService,
) (http.Handler, error) {
m := router.CompleteApp()
@ -199,9 +201,11 @@ func New(
m.Get(router.CompleteIndex).Handler(r.StaticHTML("landing/index.tmpl"))
m.Get(router.CompleteAbout).Handler(r.StaticHTML("landing/about.tmpl"))
var nr noticeRenderer
var nr noticeHandler
nr.notices = ns
m.Get(router.CompleteNotice).Handler(r.HTML("notice.tmpl", nr.render))
nr.pinned = ps
m.Get(router.CompleteNoticeList).Handler(r.HTML("notice/list.tmpl", nr.list))
m.Get(router.CompleteNoticeShow).Handler(r.HTML("notice/show.tmpl", nr.show))
m.PathPrefix("/assets/").Handler(http.StripPrefix("/assets/", http.FileServer(web.Assets)))

View File

@ -11,30 +11,43 @@ import (
"github.com/ssb-ngi-pointer/go-ssb-room/web/errors"
)
type noticeRenderer struct {
type noticeHandler struct {
pinned admindb.PinnedNoticesService
notices admindb.NoticesService
}
type noticeData struct {
func (h noticeHandler) list(rw http.ResponseWriter, req *http.Request) (interface{}, error) {
lst, err := h.pinned.List(req.Context())
if err != nil {
return nil, err
}
return struct {
Entries admindb.SortedPinnedNotices
}{lst.Sorted()}, nil
}
type noticeShowData struct {
ID int64
Title, Language string
Content template.HTML
}
func (pr noticeRenderer) render(rw http.ResponseWriter, req *http.Request) (interface{}, error) {
func (h noticeHandler) show(rw http.ResponseWriter, req *http.Request) (interface{}, error) {
noticeID, err := strconv.ParseInt(req.URL.Query().Get("id"), 10, 64)
if err != nil {
return nil, errors.ErrBadRequest{Where: "notice ID", Details: err}
}
notice, err := pr.notices.GetByID(req.Context(), noticeID)
notice, err := h.notices.GetByID(req.Context(), noticeID)
if err != nil {
return nil, err
}
markdown := blackfriday.Run([]byte(notice.Content), blackfriday.WithNoExtensions())
return noticeData{
return noticeShowData{
ID: noticeID,
Title: notice.Title,
Content: template.HTML(markdown),

View File

@ -1,6 +1,8 @@
Confirm = "Confirm"
Save = "Save"
Preview = "Preview"
GenericConfirm = "Yes"
GenericGoBack = "Back"
GenericSave = "Save"
GenericPreview = "Preview"
PageNotFound = "The requested page was not found."
LandingTitle = "ohai my room"
@ -15,9 +17,6 @@ AuthFallbackTitle = "The place of last resort"
AuthSignIn = "Sign in"
AuthSignOut = "Sign out"
GenericConfirm = "Yes"
GenericGoBack = "Back"
AdminDashboardWelcome = "Welcome to your dashboard"
AdminDashboardTitle = "Room Admin Dashboard"
@ -30,13 +29,14 @@ AdminAllowListRemoveConfirmTitle = "Confirm member removal"
NavAdminDashboard = "Dashboard"
NoticeEditTitle = "Edit Notice"
NoticeList = "List of notices"
[MemberCount]
description = "Number of members"
one = "1 member"
other = "{{.Count}} members"
NoticeEditTitle = "Edit Notice"
[ListCount]
description = "generic list"
one = "There is one item on the List"

View File

@ -8,9 +8,11 @@ import (
// constant names for the named routes
const (
CompleteIndex = "complete:index"
CompleteAbout = "complete:about"
CompleteNotice = "complete:notice"
CompleteIndex = "complete:index"
CompleteAbout = "complete:about"
CompleteNoticeShow = "complete:notice:show"
CompleteNoticeList = "complete:notice:list"
)
// CompleteApp constructs a mux.Router containing the routes for batch Complete html frontend
@ -23,7 +25,8 @@ func CompleteApp() *mux.Router {
m.Path("/").Methods("GET").Name(CompleteIndex)
m.Path("/about").Methods("GET").Name(CompleteAbout)
m.Path("/notice").Methods("GET").Name(CompleteNotice)
m.Path("/notice/show").Methods("GET").Name(CompleteNoticeShow)
m.Path("/notice/list").Methods("GET").Name(CompleteNoticeList)
return m
}

View File

@ -33,11 +33,11 @@
<button
type="submit"
class="shadow rounded px-4 h-8 text-gray-100 bg-pink-600 hover:bg-pink-700 focus:outline-none focus:ring-2 focus:ring-pink-600 focus:ring-opacity-50"
>{{i18n "Save"}}</button>
>{{i18n "GenericSave"}}</button>
</form>
</div>
<div>
<h1>{{i18n "Preview"}}</h1>
<h1>{{i18n "GenericPreview"}}</h1>
{{.ContentPreview}}
</div>

View File

@ -0,0 +1,18 @@
{{ define "title" }}{{i18n "NoticeList"}}{{ end }}
{{ define "content" }}
<div class="container mx-auto">
<h1 class="text-lg">{{i18n "NoticeList"}}</h1>
<ul id="theList">
{{range .Entries}}
<li>
<a href="{{urlTo "complete:notice:show" "id" .ID}}">
Title: {{.Title}}
</a>
</li>
{{end}}
</ul>
</div>
{{end}}