Skip to content

Commit

Permalink
Progres on search tags
Browse files Browse the repository at this point in the history
  • Loading branch information
benpate committed Nov 14, 2024
1 parent 992295a commit de47300
Show file tree
Hide file tree
Showing 12 changed files with 554 additions and 4 deletions.
191 changes: 191 additions & 0 deletions build/builder_admin_tags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
package build

import (
"bytes"
"html/template"
"net/http"

"github.com/EmissarySocial/emissary/model"
"github.com/EmissarySocial/emissary/service"
"github.com/benpate/data"
"github.com/benpate/derp"
"github.com/benpate/exp"
builder "github.com/benpate/exp-builder"
"github.com/benpate/rosetta/schema"
"github.com/rs/zerolog/log"
"go.mongodb.org/mongo-driver/bson/primitive"
)

// SearchTag is a builder for the admin/searchTags page
// It can only be accessed by a Domain Owner
type SearchTag struct {
_searchTag *model.SearchTag
CommonWithTemplate
}

// NewSearchTag returns a fully initialized `SearchTag` builder.
func NewSearchTag(factory Factory, request *http.Request, response http.ResponseWriter, searchTag *model.SearchTag, template model.Template, actionID string) (SearchTag, error) {

const location = "build.NewSearchTag"

// Create the underlying Common builder
common, err := NewCommonWithTemplate(factory, request, response, template, actionID)

if err != nil {
return SearchTag{}, derp.Wrap(err, location, "Error creating common builder")
}

// Verify that the user is a Domain Owner
if !common._authorization.DomainOwner {
return SearchTag{}, derp.NewForbiddenError(location, "Must be domain owner to continue")
}

// Return the SearchTag builder
return SearchTag{
_searchTag: searchTag,
CommonWithTemplate: common,
}, nil
}

/******************************************
* Renderer Interface
******************************************/

// Render generates the string value for this Stream
func (w SearchTag) Render() (template.HTML, error) {

var buffer bytes.Buffer

// Execute step (write HTML to buffer, update context)
status := Pipeline(w._action.Steps).Get(w._factory, &w, &buffer)

if status.Error != nil {
err := derp.Wrap(status.Error, "build.SearchTag.Render", "Error generating HTML")
derp.Report(err)
return "", err
}

// Success!
status.Apply(w._response)
return template.HTML(buffer.String()), nil
}

// View executes a separate view for this SearchTag
func (w SearchTag) View(actionID string) (template.HTML, error) {

const location = "build.SearchTag.View"

builder, err := NewSearchTag(w._factory, w._request, w._response, w._searchTag, w._template, actionID)

if err != nil {
return template.HTML(""), derp.Wrap(err, location, "Error creating SearchTag builder")
}

return builder.Render()
}

func (w SearchTag) NavigationID() string {
return "admin"
}

func (w SearchTag) Permalink() string {
return w.Hostname() + "/admin/searchTags/" + w.SearchTagID()
}

func (w SearchTag) BasePath() string {
return "/admin/searchTags/" + w.SearchTagID()
}

func (w SearchTag) Token() string {
return "searchTags"
}

func (w SearchTag) PageTitle() string {
return "Settings"
}

func (w SearchTag) object() data.Object {
return w._searchTag
}

func (w SearchTag) objectID() primitive.ObjectID {
return w._searchTag.SearchTagID
}

func (w SearchTag) objectType() string {
return "SearchTag"
}

func (w SearchTag) schema() schema.Schema {
return schema.New(model.SearchTagSchema())
}

func (w SearchTag) service() service.ModelService {
return w._factory.SearchTag()
}

func (w SearchTag) clone(action string) (Builder, error) {
return NewSearchTag(w._factory, w._request, w._response, w._searchTag, w._template, action)
}

/******************************************
* DATA ACCESSORS
******************************************/

func (w SearchTag) SearchTagID() string {
if w._searchTag == nil {
return ""
}
return w._searchTag.SearchTagID.Hex()
}

func (w SearchTag) Tag() string {
if w._searchTag == nil {
return ""
}
return w._searchTag.Tag
}

/******************************************
* QUERY BUILDERS
******************************************/

func (w SearchTag) SearchTags() *QueryBuilder[model.SearchTag] {

query := builder.NewBuilder().
String("type").
String("behavior").
String("trigger")

criteria := exp.And(
query.Evaluate(w._request.URL.Query()),
exp.Equal("userId", w._authorization.UserID),
exp.Equal("deleteDate", 0),
)

result := NewQueryBuilder[model.SearchTag](w._factory.SearchTag(), criteria)

return &result
}

func (w SearchTag) ServerWideSearchTags() *QueryBuilder[model.SearchTag] {

query := builder.NewBuilder().
String("type").
String("behavior").
String("trigger")

criteria := exp.And(
query.Evaluate(w._request.URL.Query()),
exp.Equal("userId", primitive.NilObjectID),
exp.Equal("deleteDate", 0),
)

result := NewQueryBuilder[model.SearchTag](w._factory.SearchTag(), criteria)

return &result
}

func (w SearchTag) debug() {
log.Debug().Interface("object", w.object()).Msg("builder_admin_searchTag")
}
4 changes: 0 additions & 4 deletions build/builder_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,6 @@ func (w Model) Object() any {
return w._object
}

func (w Model) ObjectID() string {
return w._object.ID()
}

func (w Model) Label() string {
switch object := w._object.(type) {

Expand Down
1 change: 1 addition & 0 deletions build/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type Factory interface {
Registration() *service.Registration
Response() *service.Response
Rule() *service.Rule
SearchTag() *service.SearchTag
Stream() *service.Stream
StreamDraft() *service.StreamDraft
Template() *service.Template
Expand Down
3 changes: 3 additions & 0 deletions domain/collections.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ const CollectionOAuthUserToken = "OAuthUserToken"
// CollectionOutbox is the name of the database collection where users' Outbox records are stored
const CollectionOutbox = "Outbox"

// CollectionSearchTag is the name of the database collection where SearchTags are stored
const CollectionSearchTag = "SearchTag"

// CollectionStream is the name of the database collection where Streams are stored
const CollectionStream = "Stream"

Expand Down
7 changes: 7 additions & 0 deletions domain/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ type Factory struct {
outboxService service.Outbox
responseService service.Response
ruleService service.Rule
searchTagService service.SearchTag
streamService service.Stream
streamDraftService service.StreamDraft
realtimeBroker RealtimeBroker
Expand Down Expand Up @@ -133,6 +134,7 @@ func NewFactory(domain config.Domain, port string, providers []config.Provider,
factory.outboxService = service.NewOutbox()
factory.responseService = service.NewResponse()
factory.ruleService = service.NewRule()
factory.searchTagService = service.NewSearchTag()
factory.streamService = service.NewStream()
factory.streamDraftService = service.NewStreamDraft()
factory.userService = service.NewUser()
Expand Down Expand Up @@ -335,6 +337,11 @@ func (factory *Factory) Refresh(domain config.Domain, providers []config.Provide
factory.Host(),
)

factory.searchTagService.Refresh(
factory.collection(CollectionSearchTag),
factory.Host(),
)

// Populate Stream Service
factory.streamService.Refresh(
factory.collection(CollectionStream),
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ module github.com/EmissarySocial/emissary

go 1.23.1

replace github.com/benpate/data => ../../benpate/data

require (
github.com/PuerkitoBio/goquery v1.10.0
github.com/aws/aws-sdk-go v1.55.5
Expand Down
18 changes: 18 additions & 0 deletions model/searchResult.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package model

import (
"github.com/benpate/data/journal"
"go.mongodb.org/mongo-driver/bson/primitive"
)

// SearchResult represents a value in the search index
type SearchResult struct {
SearchResultID primitive.ObjectID `bson:"_id"` // SearchResultID is the unique identifier for a SearchResult.
StreamID primitive.ObjectID `bson:"streamId"` // StreamID is the ID of the stream that this SearchResult is associated with.
URL string `bson:"url"` // URL is the URL of the SearchResult.
Name string `bson:"name"` // Name is the name of the SearchResult.
IconURL string `bson:"icon"` // IconURL is the URL of the icon for the SearchResult.
Tags []string `bson:"tags"` // Tags is a list of tags that are associated with this SearchResult.
Type int `bson:"type"` // Type is the type of the SearchResult. (ACTOR, OBJECT)
journal.Journal `bson:",inline"`
}
41 changes: 41 additions & 0 deletions model/searchTag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package model

import (
"github.com/benpate/data/journal"
"go.mongodb.org/mongo-driver/bson/primitive"
)

// SearchTag represents a tag that Users and Guests can use to search
// for streams in the database.
type SearchTag struct {
SearchTagID primitive.ObjectID `bson:"_id"` // SearchTagID is the unique identifier for a SearchTag.
ParentID primitive.ObjectID `bson:"parentId"` // ParentID is the ID of the parent SearchTag.
Tag string `bson:"tag"` // Tag is the name of the tag.
Notes string `bson:"notes"` // Notes is a place for administrators to make notes about the tag.
StateID int `bson:"stateId"` // StateID represents the state that the tag is in. (FEATURED, ALLOWED, WAITING, BLOCKED)
Rank int `bson:"rank"` // Rank is the sort order of the SearchTag.

journal.Journal `bson:",inline"`
}

// NewSearchTag returns a fully initialized SearchTag object
func NewSearchTag() SearchTag {
return SearchTag{
SearchTagID: primitive.NewObjectID(),
StateID: SearchTagStateWaiting,
}
}

// ID returns the unique identifier for this SearchTag,
// implementing the data.Object interface.
func (searchTag SearchTag) ID() string {
return searchTag.SearchTagID.Hex()
}

func (searchTag SearchTag) Fields() []string {
return []string{
"_id",
"tag",
"stateId",
}
}
Loading

0 comments on commit de47300

Please sign in to comment.