diff --git a/lemmy.go b/lemmy.go index 8705dc4..6ee38f8 100644 --- a/lemmy.go +++ b/lemmy.go @@ -13,16 +13,19 @@ import ( "go.arsenm.dev/go-lemmy/types" ) +// Client is a client for Lemmy's HTTP API type Client struct { client *http.Client baseURL *url.URL - token string + Token string } +// New creates a new Lemmy client with the default HTTP client. func New(baseURL string) (*Client, error) { return NewWithClient(baseURL, http.DefaultClient) } +// NewWithClient creates a new Lemmy client with the given HTTP client func NewWithClient(baseURL string, client *http.Client) (*Client, error) { u, err := url.Parse(baseURL) if err != nil { @@ -33,6 +36,9 @@ func NewWithClient(baseURL string, client *http.Client) (*Client, error) { return &Client{baseURL: u, client: client}, nil } +// Login logs in to Lemmy by sending an HTTP request to the +// login endpoint. It stores the returned token in the client +// for future use. func (c *Client) Login(ctx context.Context, l types.Login) error { var lr types.LoginResponse res, err := c.req(ctx, http.MethodPost, "/user/login", l, &lr) @@ -45,19 +51,12 @@ func (c *Client) Login(ctx context.Context, l types.Login) error { return err } - c.token = lr.JWT.MustValue() + c.Token = lr.JWT.MustValue() return nil } -func (c *Client) Token() string { - return c.token -} - -func (c *Client) SetToken(t string) { - c.token = t -} - +// req makes a request to the server func (c *Client) req(ctx context.Context, method string, path string, data, resp any) (*http.Response, error) { data = c.setAuth(data) @@ -98,6 +97,9 @@ func (c *Client) req(ctx context.Context, method string, path string, data, resp return res, nil } +// getReq makes a get request to the Lemmy server. +// It is separate from req() because it uses query +// parameters rather than a JSON request body. func (c *Client) getReq(ctx context.Context, method string, path string, data, resp any) (*http.Response, error) { data = c.setAuth(data) @@ -134,6 +136,7 @@ func (c *Client) getReq(ctx context.Context, method string, path string, data, r return res, nil } +// resError returns an error if the given response is an error func resError(res *http.Response, lr types.LemmyResponse) error { if lr.Error.IsValid() { return types.LemmyError{ @@ -149,6 +152,11 @@ func resError(res *http.Response, lr types.LemmyResponse) error { } } +// setAuth uses reflection to automatically +// set struct fields called Auth of type +// string or types.Optional[string] to the +// authentication token, then returns the +// updated struct func (c *Client) setAuth(data any) any { if data == nil { return data @@ -164,10 +172,10 @@ func (c *Client) setAuth(data any) any { switch authField.Type().String() { case "string": - authField.SetString(c.token) + authField.SetString(c.Token) case "types.Optional[string]": setMtd := authField.MethodByName("Set") - out := setMtd.Call([]reflect.Value{reflect.ValueOf(c.token)}) + out := setMtd.Call([]reflect.Value{reflect.ValueOf(c.Token)}) authField.Set(out[0]) default: return data diff --git a/person.go b/person.go index fae4012..386447e 100644 --- a/person.go +++ b/person.go @@ -49,7 +49,7 @@ func (c *Client) ChangePassword(ctx context.Context, d types.ChangePassword) (*t return nil, err } - c.token = ar.JWT.MustValue() + c.Token = ar.JWT.MustValue() return ar, nil } @@ -231,7 +231,7 @@ func (c *Client) PasswordChange(ctx context.Context, d types.PasswordChange) (*t return nil, err } - c.token = ar.JWT.MustValue() + c.Token = ar.JWT.MustValue() return ar, nil } @@ -263,7 +263,7 @@ func (c *Client) Register(ctx context.Context, d types.Register) (*types.LoginRe return nil, err } - c.token = ar.JWT.MustValue() + c.Token = ar.JWT.MustValue() return ar, nil } @@ -280,7 +280,7 @@ func (c *Client) SaveUserSettings(ctx context.Context, d types.SaveUserSettings) return nil, err } - c.token = ar.JWT.MustValue() + c.Token = ar.JWT.MustValue() return ar, nil } diff --git a/websocket.go b/websocket.go index 5f9484a..66f2ce0 100644 --- a/websocket.go +++ b/websocket.go @@ -15,14 +15,17 @@ type authData struct { Auth string `json:"auth"` } +// WSClient is a client for Lemmy's WebSocket API type WSClient struct { conn *websocket.Conn baseURL *url.URL respCh chan types.LemmyWebSocketMsg errCh chan error - token string + Token string } +// NewWebSocket creates and returns a new WSClient, and +// starts a goroutine to read server responses and errors func NewWebSocket(baseURL string) (*WSClient, error) { u, err := url.Parse(baseURL) if err != nil { @@ -57,6 +60,9 @@ func NewWebSocket(baseURL string) (*WSClient, error) { return out, nil } +// Login logs in to Lemmy by sending an HTTP request to the +// login endpoint. It stores the returned token in the client +// for future use. func (c *WSClient) Login(ctx context.Context, l types.Login) error { u := &url.URL{} *u = *c.baseURL @@ -72,10 +78,14 @@ func (c *WSClient) Login(ctx context.Context, l types.Login) error { if err != nil { return err } - c.token = hc.token + c.Token = hc.Token return nil } +// Request sends a request to the server. If data is nil, +// the authentication token will be sent instead. If data +// has an Auth field, it will be set to the authentication +// token automatically. func (c *WSClient) Request(op types.UserOperation, data any) error { if data == nil { data = authData{} @@ -94,14 +104,23 @@ func (c *WSClient) Request(op types.UserOperation, data any) error { }) } +// Responses returns a channel that receives messages from +// Lemmy. func (c *WSClient) Responses() <-chan types.LemmyWebSocketMsg { return c.respCh } +// Errors returns a channel that receives errors +// received while attempting to read responses func (c *WSClient) Errors() <-chan error { return c.errCh } +// setAuth uses reflection to automatically +// set struct fields called Auth of type +// string or types.Optional[string] to the +// authentication token, then returns the +// updated struct func (c *WSClient) setAuth(data any) any { val := reflect.New(reflect.TypeOf(data)) val.Elem().Set(reflect.ValueOf(data)) @@ -113,10 +132,10 @@ func (c *WSClient) setAuth(data any) any { switch authField.Type().String() { case "string": - authField.SetString(c.token) + authField.SetString(c.Token) case "types.Optional[string]": setMtd := authField.MethodByName("Set") - out := setMtd.Call([]reflect.Value{reflect.ValueOf(c.token)}) + out := setMtd.Call([]reflect.Value{reflect.ValueOf(c.Token)}) authField.Set(out[0]) default: return data