7 changed files with 407 additions and 3 deletions
			
			
		- 
					42cmd/rpdata-ensurechannels/main.go
- 
					1makefile
- 
					133model/channel/channel.go
- 
					12model/log/log.go
- 
					154resolver/channel.go
- 
					17schema/root.graphql
- 
					51schema/types/channel.graphql
| @ -0,0 +1,42 @@ | |||
| package main | |||
| 
 | |||
| import ( | |||
| 	"fmt" | |||
| 	"os" | |||
| 
 | |||
| 	"git.aiterp.net/rpdata/api/model/channel" | |||
| 
 | |||
| 	"git.aiterp.net/rpdata/api/internal/store" | |||
| 	"git.aiterp.net/rpdata/api/model/log" | |||
| ) | |||
| 
 | |||
| func main() { | |||
| 	err := store.Init() | |||
| 	if err != nil { | |||
| 		fmt.Fprintln(os.Stderr, err) | |||
| 		return | |||
| 	} | |||
| 
 | |||
| 	logs, err := log.List(32768) | |||
| 	if err != nil { | |||
| 		fmt.Fprintln(os.Stderr, err) | |||
| 		return | |||
| 	} | |||
| 
 | |||
| 	added := make(map[string]bool, 1024) | |||
| 	for _, log := range logs { | |||
| 		if added[log.Channel] { | |||
| 			continue | |||
| 		} | |||
| 
 | |||
| 		_, err := channel.Ensure(log.Channel, false) | |||
| 		if err != nil { | |||
| 			fmt.Fprintln(os.Stderr, log.ID, err) | |||
| 			continue | |||
| 		} | |||
| 
 | |||
| 		added[log.Channel] = true | |||
| 
 | |||
| 		fmt.Println(log.Channel, "ensured") | |||
| 	} | |||
| } | |||
| @ -0,0 +1,133 @@ | |||
| package channel | |||
| 
 | |||
| import ( | |||
| 	"errors" | |||
| 	"strings" | |||
| 
 | |||
| 	"git.aiterp.net/rpdata/api/internal/store" | |||
| 	"github.com/globalsign/mgo" | |||
| 	"github.com/globalsign/mgo/bson" | |||
| ) | |||
| 
 | |||
| var collection *mgo.Collection | |||
| 
 | |||
| // ErrInvalidName is an error for an invalid channel name
 | |||
| var ErrInvalidName = errors.New("Invalid channel name") | |||
| 
 | |||
| // A Channel represents information abount an IRC RP channel, and whether it should be logged
 | |||
| type Channel struct { | |||
| 	Name     string `bson:"_id"` | |||
| 	Logged   bool   `bson:"logged"` | |||
| 	Hub      bool   `bson:"hub"` | |||
| 	Event    string `bson:"event,omitempty"` | |||
| 	Location string `bson:"location,omitempty"` | |||
| } | |||
| 
 | |||
| // Edit edits the channel
 | |||
| func (channel *Channel) Edit(logged, hub *bool, event, location *string) error { | |||
| 	changes := bson.M{} | |||
| 	changed := *channel | |||
| 
 | |||
| 	if logged != nil && channel.Logged != *logged { | |||
| 		changes["logged"] = *logged | |||
| 		changed.Logged = *logged | |||
| 	} | |||
| 	if hub != nil && channel.Hub != *hub { | |||
| 		changes["hub"] = *hub | |||
| 		changed.Hub = *hub | |||
| 	} | |||
| 	if event != nil && channel.Event != *event { | |||
| 		changes["event"] = *event | |||
| 		changed.Event = *event | |||
| 	} | |||
| 	if location != nil && channel.Event != *location { | |||
| 		changes["location"] = *location | |||
| 		changed.Location = *location | |||
| 	} | |||
| 
 | |||
| 	if len(changes) == 0 { | |||
| 		return nil | |||
| 	} | |||
| 
 | |||
| 	err := collection.UpdateId(channel.Name, bson.M{"$set": changes}) | |||
| 	if err != nil { | |||
| 		return err | |||
| 	} | |||
| 
 | |||
| 	*channel = changed | |||
| 
 | |||
| 	return nil | |||
| } | |||
| 
 | |||
| // Remove removes the channel information from the database.
 | |||
| func (channel *Channel) Remove() error { | |||
| 	return collection.RemoveId(channel.Name) | |||
| } | |||
| 
 | |||
| // Ensure ensures a channel's existence. It does not change `logged` if there is
 | |||
| // an existing channel.
 | |||
| func Ensure(name string, logged bool) (Channel, error) { | |||
| 	channel, err := FindName(name) | |||
| 	if err == mgo.ErrNotFound { | |||
| 		return New(name, logged, false, "", "") | |||
| 	} else if err != nil { | |||
| 		return Channel{}, err | |||
| 	} | |||
| 
 | |||
| 	return channel, nil | |||
| } | |||
| 
 | |||
| // New creates a new channel
 | |||
| func New(name string, logged, hub bool, event, location string) (Channel, error) { | |||
| 	if len(name) < 3 && !strings.HasPrefix(name, "#") { | |||
| 		return Channel{}, ErrInvalidName | |||
| 	} | |||
| 
 | |||
| 	channel := Channel{ | |||
| 		Name:     name, | |||
| 		Logged:   logged, | |||
| 		Hub:      hub, | |||
| 		Event:    event, | |||
| 		Location: location, | |||
| 	} | |||
| 
 | |||
| 	err := collection.Insert(channel) | |||
| 	if err != nil { | |||
| 		return Channel{}, err | |||
| 	} | |||
| 
 | |||
| 	return channel, nil | |||
| } | |||
| 
 | |||
| // FindName finds a channel by its id (its name).
 | |||
| func FindName(name string) (Channel, error) { | |||
| 	channel := Channel{} | |||
| 	err := collection.FindId(name).One(&channel) | |||
| 
 | |||
| 	return channel, err | |||
| } | |||
| 
 | |||
| // List finds channels, if logged is true it will be limited to logged
 | |||
| // channels
 | |||
| func List(logged bool) ([]Channel, error) { | |||
| 	query := bson.M{} | |||
| 	if logged { | |||
| 		query["logged"] = true | |||
| 	} | |||
| 
 | |||
| 	channels := make([]Channel, 0, 32) | |||
| 	err := collection.Find(query).All(&channels) | |||
| 
 | |||
| 	return channels, err | |||
| } | |||
| 
 | |||
| func init() { | |||
| 	store.HandleInit(func(db *mgo.Database) { | |||
| 		collection = db.C("common.channels") | |||
| 
 | |||
| 		collection.EnsureIndexKey("logged") | |||
| 		collection.EnsureIndexKey("hub") | |||
| 		collection.EnsureIndexKey("event") | |||
| 		collection.EnsureIndexKey("location") | |||
| 	}) | |||
| } | |||
| @ -0,0 +1,154 @@ | |||
| package resolver | |||
| 
 | |||
| import ( | |||
| 	"context" | |||
| 
 | |||
| 	"git.aiterp.net/rpdata/api/internal/session" | |||
| 	"git.aiterp.net/rpdata/api/model/channel" | |||
| ) | |||
| 
 | |||
| // ChannelResolver for the Channel graphql type
 | |||
| type ChannelResolver struct{ C channel.Channel } | |||
| 
 | |||
| // ChannelArgs is args for channel query
 | |||
| type ChannelArgs struct { | |||
| 	Name string | |||
| } | |||
| 
 | |||
| // Channel implements the channel query
 | |||
| func (r *QueryResolver) Channel(ctx context.Context, args *ChannelArgs) (*ChannelResolver, error) { | |||
| 	channel, err := channel.FindName(args.Name) | |||
| 	if err != nil { | |||
| 		return nil, err | |||
| 	} | |||
| 
 | |||
| 	return &ChannelResolver{C: channel}, nil | |||
| } | |||
| 
 | |||
| // ChannelsArgs is args for channel query
 | |||
| type ChannelsArgs struct { | |||
| 	Logged *bool | |||
| } | |||
| 
 | |||
| // Channels implements the channels query
 | |||
| func (r *QueryResolver) Channels(ctx context.Context, args *ChannelsArgs) ([]*ChannelResolver, error) { | |||
| 	channels, err := channel.List(args.Logged != nil && *args.Logged) | |||
| 	if err != nil { | |||
| 		return nil, err | |||
| 	} | |||
| 
 | |||
| 	resolvers := make([]*ChannelResolver, len(channels)) | |||
| 	for i := range channels { | |||
| 		resolvers[i] = &ChannelResolver{C: channels[i]} | |||
| 	} | |||
| 
 | |||
| 	return resolvers, nil | |||
| } | |||
| 
 | |||
| // ChannelAddEditInput is input for the addChannel mutation
 | |||
| type ChannelAddEditInput struct { | |||
| 	Name         string | |||
| 	Logged       *bool | |||
| 	Hub          *bool | |||
| 	EventName    *string | |||
| 	LocationName *string | |||
| } | |||
| 
 | |||
| // AddChannel resolves the addChannel mutation
 | |||
| func (r *MutationResolver) AddChannel(ctx context.Context, args struct{ Input *ChannelAddEditInput }) (*ChannelResolver, error) { | |||
| 	user := session.FromContext(ctx).User() | |||
| 	if user == nil || !user.Permitted("channel.add") { | |||
| 		return nil, ErrUnauthorized | |||
| 	} | |||
| 
 | |||
| 	logged := args.Input.Logged != nil && *args.Input.Logged | |||
| 	hub := args.Input.Hub != nil && *args.Input.Hub | |||
| 	eventName := "" | |||
| 	if args.Input.EventName != nil { | |||
| 		eventName = *args.Input.EventName | |||
| 	} | |||
| 	locationName := "" | |||
| 	if args.Input.LocationName != nil { | |||
| 		locationName = *args.Input.LocationName | |||
| 	} | |||
| 
 | |||
| 	channel, err := channel.New(args.Input.Name, logged, hub, eventName, locationName) | |||
| 	if err != nil { | |||
| 		return nil, err | |||
| 	} | |||
| 
 | |||
| 	return &ChannelResolver{C: channel}, nil | |||
| } | |||
| 
 | |||
| // EditChannel resolves the editChannel mutation
 | |||
| func (r *MutationResolver) EditChannel(ctx context.Context, args struct{ Input *ChannelAddEditInput }) (*ChannelResolver, error) { | |||
| 	user := session.FromContext(ctx).User() | |||
| 	if user == nil || !user.Permitted("channel.edit") { | |||
| 		return nil, ErrUnauthorized | |||
| 	} | |||
| 
 | |||
| 	channel, err := channel.FindName(args.Input.Name) | |||
| 	if err != nil { | |||
| 		return nil, err | |||
| 	} | |||
| 
 | |||
| 	err = channel.Edit(args.Input.Logged, args.Input.Hub, args.Input.EventName, args.Input.LocationName) | |||
| 	if err != nil { | |||
| 		return nil, err | |||
| 	} | |||
| 
 | |||
| 	return &ChannelResolver{C: channel}, nil | |||
| } | |||
| 
 | |||
| // RemoveChannel resolves the editChannel mutation
 | |||
| func (r *MutationResolver) RemoveChannel(ctx context.Context, args ChannelArgs) (*ChannelResolver, error) { | |||
| 	user := session.FromContext(ctx).User() | |||
| 	if user == nil || !user.Permitted("channel.remove") { | |||
| 		return nil, ErrUnauthorized | |||
| 	} | |||
| 
 | |||
| 	channel, err := channel.FindName(args.Name) | |||
| 	if err != nil { | |||
| 		return nil, err | |||
| 	} | |||
| 
 | |||
| 	err = channel.Remove() | |||
| 	if err != nil { | |||
| 		return nil, err | |||
| 	} | |||
| 
 | |||
| 	return &ChannelResolver{C: channel}, nil | |||
| } | |||
| 
 | |||
| // Name resolves channel.name
 | |||
| func (r *ChannelResolver) Name() string { | |||
| 	return r.C.Name | |||
| } | |||
| 
 | |||
| // Logged resolves channel.logged
 | |||
| func (r *ChannelResolver) Logged() bool { | |||
| 	return r.C.Logged | |||
| } | |||
| 
 | |||
| // Hub resolves channel.hub
 | |||
| func (r *ChannelResolver) Hub() bool { | |||
| 	return r.C.Hub | |||
| } | |||
| 
 | |||
| // EventName resolves channel.eventName
 | |||
| func (r *ChannelResolver) EventName() *string { | |||
| 	if r.C.Event == "" { | |||
| 		return nil | |||
| 	} | |||
| 
 | |||
| 	return &r.C.Event | |||
| } | |||
| 
 | |||
| // LocationName resolves channel.locationName
 | |||
| func (r *ChannelResolver) LocationName() *string { | |||
| 	if r.C.Location == "" { | |||
| 		return nil | |||
| 	} | |||
| 
 | |||
| 	return &r.C.Location | |||
| } | |||
| @ -0,0 +1,51 @@ | |||
| # Information about an IRC channel | |||
| type Channel { | |||
|   # The channel's name | |||
|   name: String! | |||
| 
 | |||
|   # Whether the channel should be logged | |||
|   logged: Boolean! | |||
| 
 | |||
|   # Whether the channel is a hub channel | |||
|   hub: Boolean! | |||
| 
 | |||
|   # The event name, or `null` if none is specified | |||
|   eventName: String | |||
| 
 | |||
|   # The location name, or `null` if none is specified | |||
|   locationName: String | |||
| } | |||
| 
 | |||
| input ChannelAddInput { | |||
|   # The channel's name | |||
|   name: String! | |||
|    | |||
|   # Whether the channel should be logged | |||
|   logged: Boolean | |||
| 
 | |||
|   # Whether the channel is a hub channel | |||
|   hub: Boolean | |||
| 
 | |||
|   # The event name, or `null` if none is specified | |||
|   eventName: String | |||
| 
 | |||
|   # The location name, or `null` if none is specified | |||
|   locationName: String | |||
| } | |||
| 
 | |||
| input ChannelEditInput { | |||
|   # The channel's name | |||
|   name: String! | |||
|    | |||
|   # Whether the channel should be logged | |||
|   logged: Boolean | |||
| 
 | |||
|   # Whether the channel is a hub channel | |||
|   hub: Boolean | |||
| 
 | |||
|   # The event name, or `null` if none is specified | |||
|   eventName: String | |||
| 
 | |||
|   # The location name, or `null` if none is specified | |||
|   locationName: String | |||
| } | |||
						Write
						Preview
					
					
					Loading…
					
					Cancel
						Save
					
		Reference in new issue