Add channels
This commit is contained in:
parent
3f533d568a
commit
e40e2a3245
@ -7,7 +7,10 @@ import (
|
||||
var channelCmd = &cobra.Command{
|
||||
Use: "channel",
|
||||
Short: "Manage channels",
|
||||
Long: `
|
||||
Long: `Manage channels.
|
||||
|
||||
A channel is a group of sources that can be viewed together. Adding a source
|
||||
to a channel creates it and removing all sources from a channel deletes it.
|
||||
`,
|
||||
}
|
||||
|
||||
|
@ -3,19 +3,43 @@ package cmd
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/Jaculabilis/intake/core"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var channelAddCmd = &cobra.Command{
|
||||
Use: "add",
|
||||
Short: "Create a channel",
|
||||
Long: `
|
||||
Short: "Add a source to a channel",
|
||||
Long: `Add a source to a channel.
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.Fatal("not implemented")
|
||||
channelAdd(stringArg(cmd, "channel"), stringArg(cmd, "source"))
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
channelCmd.AddCommand(channelAddCmd)
|
||||
|
||||
channelAddCmd.Flags().StringP("channel", "c", "", "Channel name")
|
||||
channelAddCmd.MarkFlagRequired("channel")
|
||||
|
||||
channelAddCmd.Flags().StringP("source", "s", "", "Source to add")
|
||||
channelAddCmd.MarkFlagRequired("source")
|
||||
}
|
||||
|
||||
func channelAdd(channel string, source string) {
|
||||
if channel == "" {
|
||||
log.Fatal("error: --channel is empty")
|
||||
}
|
||||
if source == "" {
|
||||
log.Fatal("error: --source is empty")
|
||||
}
|
||||
|
||||
db := openAndMigrateDb()
|
||||
|
||||
if err := core.AddSourceToChannel(db, channel, source); err != nil {
|
||||
log.Fatalf("error: failed to add source to channel: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("Added source %s to channel %s", source, channel)
|
||||
}
|
||||
|
@ -3,19 +3,44 @@ package cmd
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/Jaculabilis/intake/core"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var channelDeleteCmd = &cobra.Command{
|
||||
Use: "delete",
|
||||
Short: "Delete a channel",
|
||||
Long: `
|
||||
Use: "remove",
|
||||
Aliases: []string{"rm"},
|
||||
Short: "Remove a source from a channel",
|
||||
Long: `Remove a source from a channel.
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.Fatal("not implemented")
|
||||
channelRemove(stringArg(cmd, "channel"), stringArg(cmd, "source"))
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
channelCmd.AddCommand(channelDeleteCmd)
|
||||
|
||||
channelDeleteCmd.Flags().StringP("channel", "c", "", "Channel name")
|
||||
channelDeleteCmd.MarkFlagRequired("channel")
|
||||
|
||||
channelDeleteCmd.Flags().StringP("source", "s", "", "Source to add")
|
||||
channelDeleteCmd.MarkFlagRequired("source")
|
||||
}
|
||||
|
||||
func channelRemove(channel string, source string) {
|
||||
if channel == "" {
|
||||
log.Fatal("error: --channel is empty")
|
||||
}
|
||||
if source == "" {
|
||||
log.Fatal("error: --source is empty")
|
||||
}
|
||||
|
||||
db := openAndMigrateDb()
|
||||
|
||||
if err := core.DeleteSourceFromChannel(db, channel, source); err != nil {
|
||||
log.Fatalf("error: failed to remove source from channel: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("Removed source %s from channel %s", source, channel)
|
||||
}
|
||||
|
@ -1,21 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var channelEditCmd = &cobra.Command{
|
||||
Use: "edit",
|
||||
Short: "Edit a channel",
|
||||
Long: `
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
log.Fatal("not implemented")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
channelCmd.AddCommand(channelEditCmd)
|
||||
}
|
45
cmd/channelList.go
Normal file
45
cmd/channelList.go
Normal file
@ -0,0 +1,45 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/Jaculabilis/intake/core"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var channelListCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Aliases: []string{"ls"},
|
||||
Short: "List channels",
|
||||
Long: `List channels.
|
||||
`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
channelList(stringArg(cmd, "channel"))
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
channelCmd.AddCommand(channelListCmd)
|
||||
|
||||
channelListCmd.Flags().StringP("channel", "c", "", "List sources in a specific channel")
|
||||
}
|
||||
|
||||
func channelList(channel string) {
|
||||
db := openAndMigrateDb()
|
||||
|
||||
channelSources, err := core.GetSourcesInChannel(db)
|
||||
if err != nil {
|
||||
log.Fatalf("error: failed to get sources in channel: %v", err)
|
||||
}
|
||||
|
||||
if channel == "" {
|
||||
for channel := range channelSources {
|
||||
fmt.Println(channel)
|
||||
}
|
||||
} else {
|
||||
for _, source := range channelSources[channel] {
|
||||
fmt.Println(source)
|
||||
}
|
||||
}
|
||||
}
|
41
core/channel.go
Normal file
41
core/channel.go
Normal file
@ -0,0 +1,41 @@
|
||||
package core
|
||||
|
||||
func AddSourceToChannel(db DB, channel string, source string) error {
|
||||
_, err := db.Exec(`
|
||||
insert into channels (name, source)
|
||||
values (?, ?)
|
||||
`, channel, source)
|
||||
return err
|
||||
}
|
||||
|
||||
func DeleteSourceFromChannel(db DB, channel string, source string) error {
|
||||
_, err := db.Exec(`
|
||||
delete from channels
|
||||
where name = ? and source = ?
|
||||
`, channel, source)
|
||||
return err
|
||||
}
|
||||
|
||||
func GetSourcesInChannel(db DB) (map[string][]string, error) {
|
||||
rows, err := db.Query(`
|
||||
select name, source
|
||||
from channels
|
||||
order by name, source
|
||||
`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
channelSources := make(map[string][]string)
|
||||
for rows.Next() {
|
||||
var name, source string
|
||||
if err := rows.Scan(&name, &source); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
channelSources[name] = append(channelSources[name], source)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return channelSources, nil
|
||||
}
|
70
core/channel_test.go
Normal file
70
core/channel_test.go
Normal file
@ -0,0 +1,70 @@
|
||||
package core
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestChannel(t *testing.T) {
|
||||
db := EphemeralDb(t)
|
||||
if err := AddSource(db, "one"); err != nil {
|
||||
t.Fatalf("failed to add source: %v", err)
|
||||
}
|
||||
if err := AddSource(db, "two"); err != nil {
|
||||
t.Fatalf("failed to add source: %v", err)
|
||||
}
|
||||
|
||||
// Add sources to channel
|
||||
if err := AddSourceToChannel(db, "channel", "one"); err != nil {
|
||||
t.Fatalf("failed to add source to channel: %v", err)
|
||||
}
|
||||
if err := AddSourceToChannel(db, "channel", "two"); err != nil {
|
||||
t.Fatalf("failed to add source to channel: %v", err)
|
||||
}
|
||||
|
||||
// Both sources are in the channel
|
||||
sources, err := GetSourcesInChannel(db)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get sources in channel: %v", err)
|
||||
}
|
||||
if len(sources["channel"]) != 2 || sources["channel"][0] != "one" || sources["channel"][1] != "two" {
|
||||
t.Fatalf("expected two sources, got %d: %v", len(sources), sources)
|
||||
}
|
||||
|
||||
// Get sources in channel after deletion
|
||||
if err := DeleteSourceFromChannel(db, "channel", "one"); err != nil {
|
||||
t.Fatalf("failed to delete source from channel: %v", err)
|
||||
}
|
||||
sources, err = GetSourcesInChannel(db)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get sources in channel: %v", err)
|
||||
}
|
||||
if len(sources) != 1 || sources["channel"][0] != "two" {
|
||||
t.Fatalf("unexpected sources in channel after deletion: %v", sources)
|
||||
}
|
||||
if err := AddSourceToChannel(db, "channel", "one"); err != nil {
|
||||
t.Fatalf("failed to add source to channel: %v", err)
|
||||
}
|
||||
|
||||
// Items on both sources appear in the channel
|
||||
if err := AddItems(db, []Item{
|
||||
{"one", "a", 0, true, "", "", "", "", 0, nil},
|
||||
{"two", "b", 0, true, "", "", "", "", 0, nil},
|
||||
}); err != nil {
|
||||
t.Fatalf("failed to add items to one: %v", err)
|
||||
}
|
||||
if _, err := DeactivateItem(db, "one", "a"); err != nil {
|
||||
t.Fatalf("failed to deactivate item: %v", err)
|
||||
}
|
||||
items, err := GetAllItemsForChannel(db, "channel")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get all items in channel: %v", err)
|
||||
}
|
||||
if len(items) != 2 || items[0].Id != "a" || items[1].Id != "b" {
|
||||
t.Fatalf("expected two items, got %d: %v", len(items), items)
|
||||
}
|
||||
items, err = GetActiveItemsForChannel(db, "channel")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get all items in channel: %v", err)
|
||||
}
|
||||
if len(items) != 1 || items[0].Id != "b" {
|
||||
t.Fatalf("expected one item, got %d: %v", len(items), items)
|
||||
}
|
||||
}
|
@ -202,3 +202,28 @@ func GetAllItemsForSource(db DB, source string) ([]Item, error) {
|
||||
order by case when time = 0 then created else time end, id
|
||||
`, source)
|
||||
}
|
||||
|
||||
func GetActiveItemsForChannel(db DB, channel string) ([]Item, error) {
|
||||
return getItems(db, `
|
||||
select
|
||||
i.source, i.id, i.created, i.active, i.title, i.author, i.body, i.link, i.time, json(i.action)
|
||||
from items i
|
||||
join channels c on i.source = c.source
|
||||
where
|
||||
c.name = ?
|
||||
and i.active <> 0
|
||||
order by case when i.time = 0 then i.created else i.time end, i.id
|
||||
`, channel)
|
||||
}
|
||||
|
||||
func GetAllItemsForChannel(db DB, channel string) ([]Item, error) {
|
||||
return getItems(db, `
|
||||
select
|
||||
i.source, i.id, i.created, i.active, i.title, i.author, i.body, i.link, i.time, json(i.action)
|
||||
from items i
|
||||
join channels c on i.source = c.source
|
||||
where
|
||||
c.name = ?
|
||||
order by case when i.time = 0 then i.created else i.time end, i.id
|
||||
`, channel)
|
||||
}
|
||||
|
@ -31,3 +31,9 @@ create table items(
|
||||
primary key (source, id),
|
||||
foreign key (source) references sources (name) on delete cascade
|
||||
) strict;
|
||||
create table channels(
|
||||
name text not null,
|
||||
source text not null,
|
||||
unique (name, source) on conflict replace
|
||||
foreign key (source) references sources (name) on delete cascade
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user