Skip to content

Commit

Permalink
(#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
ErnestNeller authored Dec 21, 2023
1 parent f575b9e commit 066962d
Show file tree
Hide file tree
Showing 6 changed files with 348 additions and 264 deletions.
145 changes: 22 additions & 123 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log/slog"
Expand All @@ -13,6 +12,8 @@ import (
)

type Client struct {
Roles *clientRoles

instance, token string
logger *slog.Logger
bodyLogger bool
Expand Down Expand Up @@ -41,14 +42,33 @@ func NewClient(instance string, token string, opts ...ClientOption) *Client {
for _, opt := range opts {
opt(client)
}

client.Roles = &clientRoles{c: client}

return client
}

func (client *Client) urlf(format string, a ...interface{}) string {
return fmt.Sprintf("%s%s", client.instance, fmt.Sprintf(format, a...))
}

func (client *Client) do(req *http.Request, dest interface{}) error {
func (client *Client) buildSendRequest(ctx context.Context, method, url string, request, reply any) error {
var body io.Reader
if request != nil {
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(request); err != nil {
return fmt.Errorf("directus: cannot encode request: %v", err)
}
body = &buf
}
req, err := http.NewRequestWithContext(ctx, method, url, body)
if err != nil {
return fmt.Errorf("directus: cannot prepare request: %v", err)
}
return client.sendRequest(req, &reply)
}

func (client *Client) sendRequest(req *http.Request, dest interface{}) error {
client.logger.Debug("directus request", "method", req.Method, "url", req.URL.String())
if client.bodyLogger && req.Body != nil {
body, err := io.ReadAll(req.Body)
Expand Down Expand Up @@ -95,124 +115,3 @@ func (client *Client) do(req *http.Request, dest interface{}) error {

return nil
}

type ItemsClient[T any] struct {
c *Client
collection string
opts []ReadOption
}

func NewItemsClient[T any](client *Client, collection string, opts ...ReadOption) *ItemsClient[T] {
return &ItemsClient[T]{
c: client,
collection: collection,
opts: opts,
}
}

type ReadOption func(req *http.Request)

func WithFields(fields ...string) ReadOption {
return func(req *http.Request) {
q := req.URL.Query()
q.Set("fields", strings.Join(fields, ","))
req.URL.RawQuery = q.Encode()
}
}

func (items *ItemsClient[T]) itemsdo(ctx context.Context, method, url string, request, reply any) error {
var body io.Reader
if request != nil {
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(request); err != nil {
return fmt.Errorf("directus: cannot encode request: %v", err)
}
body = &buf
}
req, err := http.NewRequestWithContext(ctx, method, url, body)
if err != nil {
return fmt.Errorf("directus: cannot prepare request: %v", err)
}
for _, opt := range items.opts {
opt(req)
}
return items.c.do(req, &reply)
}

func (items *ItemsClient[T]) List(ctx context.Context) ([]*T, error) {
reply := struct {
Data []*T `json:"data"`
}{}
if err := items.itemsdo(ctx, http.MethodGet, items.c.urlf("/items/%s", items.collection), nil, &reply); err != nil {
return nil, err
}
return reply.Data, nil
}

func (items *ItemsClient[T]) Get(ctx context.Context, id string) (*T, error) {
reply := struct {
Data *T `json:"data"`
}{}
if err := items.itemsdo(ctx, http.MethodGet, items.c.urlf("/items/%s/%s", items.collection, id), nil, &reply); err != nil {
var e *unexpectedStatusError
if errors.As(err, &e) && e.status == http.StatusForbidden {
return nil, fmt.Errorf("%w: %v", ErrItemNotFound, id)
}
return nil, err
}
return reply.Data, nil
}

func (items *ItemsClient[T]) Create(ctx context.Context, item *T) (*T, error) {
reply := struct {
Data *T `json:"data"`
}{}
if err := items.itemsdo(ctx, http.MethodPost, items.c.urlf("/items/%s", items.collection), item, &reply); err != nil {
return nil, err
}
return reply.Data, nil
}

func (items *ItemsClient[T]) Update(ctx context.Context, id string, item *T) (*T, error) {
reply := struct {
Data *T `json:"data"`
}{}
if err := items.itemsdo(ctx, http.MethodPatch, items.c.urlf("/items/%s/%s", items.collection, id), item, &reply); err != nil {
return nil, err
}
return reply.Data, nil
}

func (items *ItemsClient[T]) Delete(ctx context.Context, id string) error {
return items.itemsdo(ctx, http.MethodDelete, items.c.urlf("/items/%s/%s", items.collection, id), nil, nil)
}

type SingletonClient[T any] struct {
items *ItemsClient[T]
}

func NewSingletonClient[T any](client *Client, collection string, opts ...ReadOption) *SingletonClient[T] {
return &SingletonClient[T]{
items: NewItemsClient[T](client, collection, opts...),
}
}

func (s *SingletonClient[T]) Get(ctx context.Context) (*T, error) {
reply := struct {
Data *T `json:"data"`
}{}
if err := s.items.itemsdo(ctx, http.MethodGet, s.items.c.urlf("/items/%s", s.items.collection), nil, &reply); err != nil {
return nil, err
}
return reply.Data, nil
}

func (s *SingletonClient[T]) Update(ctx context.Context, item *T) (*T, error) {
reply := struct {
Data *T `json:"data"`
}{}
if err := s.items.itemsdo(ctx, http.MethodPatch, s.items.c.urlf("/items/%s", s.items.collection), item, &reply); err != nil {
return nil, err
}
return reply.Data, nil
}
141 changes: 0 additions & 141 deletions client_test.go
Original file line number Diff line number Diff line change
@@ -1,33 +1,11 @@
package directus

import (
"context"
"fmt"
"log/slog"
"os"
"testing"

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

type Regime struct {
ID string `json:"id,omitempty"`
Code string `json:"code,omitempty"`
Status string `json:"status"`
}

type RegimeWithTranslations struct {
Code string `json:"code,omitempty"`
Status string `json:"status,omitempty"`

Translations []Relation[RegimeTranslation] `json:"translations,omitempty"`
}

type RegimeTranslation struct {
Lang string `json:"languages_code"`
DisplayName string `json:"display_name"`
}

func initClient(t *testing.T) *Client {
handler := slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelDebug})

Expand All @@ -36,122 +14,3 @@ func initClient(t *testing.T) *Client {
}
return NewClient("https://compostela.admin.onetbooking.com", os.Getenv("DIRECTUS_TOKEN"), WithLogger(slog.New(handler)), WithBodyLogger())
}

func TestItemsList(t *testing.T) {
items := NewItemsClient[Regime](initClient(t), "regimes")
regimes, err := items.List(context.Background())
require.NoError(t, err)
require.NotEmpty(t, regimes)

for _, regime := range regimes {
fmt.Printf("%#v\n", regime)
}
}

func TestItemsListFields(t *testing.T) {
items := NewItemsClient[Regime](initClient(t), "regimes", WithFields("id", "code"))
regimes, err := items.List(context.Background())
require.NoError(t, err)
require.NotEmpty(t, regimes)

for _, regime := range regimes {
require.Empty(t, regime.Status)
fmt.Printf("%#v\n", regime)
}
}

func TestItemsGet(t *testing.T) {
items := NewItemsClient[Regime](initClient(t), "regimes")
regime, err := items.Get(context.Background(), "1158c0ee-bd5d-4e7b-a640-8bceca82b5db")
require.NoError(t, err)
require.NotEmpty(t, regime)

fmt.Printf("%#v\n", regime)
}

func TestItemsGetFields(t *testing.T) {
items := NewItemsClient[Regime](initClient(t), "regimes", WithFields("id", "code"))
regime, err := items.Get(context.Background(), "1158c0ee-bd5d-4e7b-a640-8bceca82b5db")
require.NoError(t, err)
require.NotEmpty(t, regime)

require.Empty(t, regime.Status)
fmt.Printf("%#v\n", regime)
}

func TestItemsGetNotFound(t *testing.T) {
items := NewItemsClient[Regime](initClient(t), "regimes")
regime, err := items.Get(context.Background(), "foo")
require.Nil(t, regime)
require.EqualError(t, err, "directus: item not found: foo")
}

func TestItemsCreate(t *testing.T) {
items := NewItemsClient[Regime](initClient(t), "regimes")
regime, err := items.Create(context.Background(), &Regime{
Code: "test",
Status: "published",
})
require.NoError(t, err)
require.NotEmpty(t, regime)

fmt.Printf("%#v\n", regime)
}

func TestItemsCreateRelationship(t *testing.T) {
items := NewItemsClient[RegimeWithTranslations](initClient(t), "regimes")
regime, err := items.Create(context.Background(), &RegimeWithTranslations{
Code: "test",
Translations: []Relation[RegimeTranslation]{
NewRelation(&RegimeTranslation{Lang: "en-GB", DisplayName: "Test"}),
NewRelation(&RegimeTranslation{Lang: "es-ES", DisplayName: "Test"}),
},
})
require.NoError(t, err)
require.NotEmpty(t, regime)

fmt.Printf("%#v\n", regime)
}

func TestItemsUpdate(t *testing.T) {
items := NewItemsClient[Regime](initClient(t), "regimes")
regime, err := items.Update(context.Background(), "1158c0ee-bd5d-4e7b-a640-8bceca82b5db", &Regime{
Status: "draft",
})
require.NoError(t, err)
require.NotEmpty(t, regime)

fmt.Printf("%#v\n", regime)
}

func TestItemsUpdateFields(t *testing.T) {
items := NewItemsClient[RegimeWithTranslations](initClient(t), "regimes", WithFields("*", "translations.*"))
req := &RegimeWithTranslations{
Status: "draft",
}
regime, err := items.Update(context.Background(), "1918e55e-8ad0-42bf-bc38-26304f0a6b1b", req)
require.NoError(t, err)
require.NotEmpty(t, regime)

fmt.Printf("%+v\n", regime)
fmt.Printf("%+v\n", regime.Translations[0].Value())
}

func TestItemsRelationNumeric(t *testing.T) {
items := NewItemsClient[RegimeWithTranslations](initClient(t), "regimes", WithFields("*"))
regime, err := items.Get(context.Background(), "1158c0ee-bd5d-4e7b-a640-8bceca82b5db")
require.NoError(t, err)
require.NotEmpty(t, regime)

fmt.Printf("%+v\n", regime)
}

func TestItemsRelationValue(t *testing.T) {
items := NewItemsClient[RegimeWithTranslations](initClient(t), "regimes", WithFields("*", "translations.*"))
regime, err := items.Get(context.Background(), "1158c0ee-bd5d-4e7b-a640-8bceca82b5db")
require.NoError(t, err)
require.NotEmpty(t, regime)

fmt.Printf("%+v\n", regime)
fmt.Printf("%+v\n", regime.Translations[0].Value())
}
Loading

0 comments on commit 066962d

Please sign in to comment.