Skip to content

Commit

Permalink
Add support for database events in Doctl (#1524)
Browse files Browse the repository at this point in the history
* Add support for database events in Doctl

* Add integration test list database events

---------

Co-authored-by: Rahul Bhardwaj <rahulbhardwaj@digitalocean.com>
  • Loading branch information
bhardwajRahul and Rahul Bhardwaj committed Apr 16, 2024
1 parent ac0c4e9 commit 75b63a5
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 0 deletions.
38 changes: 38 additions & 0 deletions commands/databases.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ For PostgreSQL and MySQL clusters, you can also provide a disk size in MiB to sc
cmd.AddCommand(databaseOptions())
cmd.AddCommand(databaseConfiguration())
cmd.AddCommand(databaseTopic())
cmd.AddCommand(databaseEvents())

return cmd
}
Expand Down Expand Up @@ -2441,3 +2442,40 @@ func RunDatabaseConfigurationUpdate(c *CmdConfig) error {
}
return nil
}

func databaseEvents() *Command {
listDatabaseEvents := `
You can get a list of database events by calling:
doctl databases events list <cluster-id>`
cmd := &Command{
Command: &cobra.Command{
Use: "events",
Short: "Display commands for listing database cluster events",
Long: `The subcommands under ` + "`" + `doctl databases events` + "`" + ` are for listing database cluster events.` + listDatabaseEvents,
},
}
cmdDatabaseEventsList := CmdBuilder(cmd, RunDatabaseEvents, "list <database-cluster-id>", "List your database cluster events", `Retrieves a list of database clusters events:`+listDatabaseEvents, Writer, aliasOpt("ls"), displayerType(&displayers.DatabaseEvents{}))

cmdDatabaseEventsList.Example = `The following example retrieves a list of databases events in a database cluster with the ID ` + "`" + `ca9f591d-f38h-5555-a0ef-1c02d1d1e35` + "`" + `: doctl databases events list ca9f591d-f38h-5555-a0ef-1c02d1d1e35`

return cmd
}

// RunDatabaseDBList retrieves a list of databases for specific database cluster
func RunDatabaseEvents(c *CmdConfig) error {
if len(c.Args) == 0 {
return doctl.NewMissingArgsErr(c.NS)
}

id := c.Args[0]

dbEvents, err := c.Databases().ListDatabaseEvents(id)
if err != nil {
return err
}

item := &displayers.DatabaseEvents{DatabaseEvents: dbEvents}
return c.Display(item)
}
1 change: 1 addition & 0 deletions commands/databases_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ func TestDatabasesCommand(t *testing.T) {
"connection",
"migrate",
"resize",
"events",
"firewalls",
"fork",
"backups",
Expand Down
44 changes: 44 additions & 0 deletions commands/displayers/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -1647,3 +1647,47 @@ func (dc *RedisConfiguration) KV() []map[string]any {

return o
}

type DatabaseEvents struct {
DatabaseEvents do.DatabaseEvents
}

var _ Displayable = &DatabaseEvents{}

func (dr *DatabaseEvents) JSON(out io.Writer) error {
return writeJSON(dr.DatabaseEvents, out)
}

func (dr *DatabaseEvents) Cols() []string {
return []string{
"ID",
"ServiceName",
"EventType",
"CreateTime",
}
}

func (dr *DatabaseEvents) ColMap() map[string]string {

return map[string]string{
"ID": "ID",
"ServiceName": "Cluster Name",
"EventType": "Type of Event",
"CreateTime": "Create Time",
}
}

func (dr *DatabaseEvents) KV() []map[string]any {
out := make([]map[string]any, 0, len(dr.DatabaseEvents))

for _, r := range dr.DatabaseEvents {
o := map[string]any{
"ID": r.ID,
"ServiceName": r.ServiceName,
"EventType": r.EventType,
"CreateTime": r.CreateTime,
}
out = append(out, o)
}
return out
}
38 changes: 38 additions & 0 deletions do/databases.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,14 @@ type DatabaseTopicPartitions struct {
Partitions []*godo.TopicPartition
}

// DatabaseEvent is a wrapper for godo.DatabaseEvent
type DatabaseEvent struct {
*godo.DatabaseEvent
}

// DatabaseEvents is a slice of DatabaseEvent
type DatabaseEvents []DatabaseEvent

// DatabasesService is an interface for interacting with DigitalOcean's Database API
type DatabasesService interface {
List() (Databases, error)
Expand Down Expand Up @@ -183,6 +191,8 @@ type DatabasesService interface {
CreateTopic(string, *godo.DatabaseCreateTopicRequest) (*DatabaseTopic, error)
UpdateTopic(string, string, *godo.DatabaseUpdateTopicRequest) error
DeleteTopic(string, string) error

ListDatabaseEvents(string) (DatabaseEvents, error)
}

type databasesService struct {
Expand Down Expand Up @@ -745,3 +755,31 @@ func (ds *databasesService) DeleteTopic(databaseID, topicName string) error {

return err
}

func (ds *databasesService) ListDatabaseEvents(databaseID string) (DatabaseEvents, error) {
f := func(opt *godo.ListOptions) ([]any, *godo.Response, error) {
list, resp, err := ds.client.Databases.ListDatabaseEvents(context.TODO(), databaseID, opt)
if err != nil {
return nil, nil, err
}

si := make([]any, len(list))
for i := range list {
si[i] = list[i]
}

return si, resp, err
}

si, err := PaginateResp(f)
if err != nil {
return nil, err
}

list := make(DatabaseEvents, len(si))
for i := range si {
r := si[i].(godo.DatabaseEvent)
list[i] = DatabaseEvent{DatabaseEvent: &r}
}
return list, nil
}
15 changes: 15 additions & 0 deletions do/mocks/DatabasesService.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

92 changes: 92 additions & 0 deletions integration/database_events_list_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package integration

import (
"fmt"
"net/http"
"net/http/httptest"
"net/http/httputil"
"os/exec"
"strings"
"testing"

"github.com/sclevine/spec"
"github.com/stretchr/testify/require"
)

var _ = suite("database/events", func(t *testing.T, when spec.G, it spec.S) {
var (
expect *require.Assertions
server *httptest.Server
)

it.Before(func() {
expect = require.New(t)

server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
switch req.URL.Path {
case "/v2/databases/some-database-id/events":
auth := req.Header.Get("Authorization")
if auth != "Bearer some-magic-token" {
w.WriteHeader(http.StatusTeapot)
}

if req.Method != http.MethodGet {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}

w.Write([]byte(databaseListEventsResponse))
default:
dump, err := httputil.DumpRequest(req, true)
if err != nil {
t.Fatal("failed to dump request")
}

t.Fatalf("received unknown request: %s", dump)
}
}))
})

when("all required flags are passed", func() {
it("lists users for the database", func() {
cmd := exec.Command(builtBinaryPath,
"-t", "some-magic-token",
"-u", server.URL,
"database",
"events",
"list",
"some-database-id",
)

output, err := cmd.CombinedOutput()
expect.NoError(err, fmt.Sprintf("received error output: %s", output))
expect.Equal(strings.TrimSpace(databaseListEventsOutput), strings.TrimSpace(string(output)))
})
})
})

const (
databaseListEventsOutput = `
ID Cluster Name Type of Event Create Time
pe8u2huh customer-events cluster_create 2020-10-29T15:57:38Z
pe8ufefuh customer-events cluster_update 2023-10-30T15:57:38Z
`
databaseListEventsResponse = `
{
"events": [
{
"id": "pe8u2huh",
"cluster_name": "customer-events",
"event_type": "cluster_create",
"create_time": "2020-10-29T15:57:38Z"
},
{
"id": "pe8ufefuh",
"cluster_name": "customer-events",
"event_type": "cluster_update",
"create_time": "2023-10-30T15:57:38Z"
}
]
}
`
)

0 comments on commit 75b63a5

Please sign in to comment.