feat: initial commit
This commit is contained in:
104
internal/client/client.go
Normal file
104
internal/client/client.go
Normal file
@ -0,0 +1,104 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gitlab.com/wpetit/goweb/api"
|
||||
"gitlab.com/wpetit/goweb/logger"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
http *http.Client
|
||||
serverURL string
|
||||
}
|
||||
|
||||
func (c *Client) apiGet(ctx context.Context, path string, result any) error {
|
||||
if err := c.apiDo(ctx, http.MethodGet, path, nil, result); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) apiPost(ctx context.Context, path string, payload any, result any) error {
|
||||
if err := c.apiDo(ctx, http.MethodPost, path, payload, result); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) apiDo(ctx context.Context, method string, path string, payload any, response any) error {
|
||||
url := c.serverURL + path
|
||||
|
||||
logger.Debug(
|
||||
ctx, "new http request",
|
||||
logger.F("method", method),
|
||||
logger.F("url", url),
|
||||
logger.F("payload", payload),
|
||||
)
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
encoder := json.NewEncoder(&buf)
|
||||
|
||||
if err := encoder.Encode(payload); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, url, &buf)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
res, err := c.http.Do(req)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
decoder := json.NewDecoder(res.Body)
|
||||
|
||||
if err := decoder.Decode(&response); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func withResponse[T any]() struct {
|
||||
Data T
|
||||
Error *api.Error
|
||||
} {
|
||||
return struct {
|
||||
Data T
|
||||
Error *api.Error
|
||||
}{}
|
||||
}
|
||||
|
||||
func joinSlice[T any](items []T) string {
|
||||
str := ""
|
||||
|
||||
for idx, item := range items {
|
||||
if idx != 0 {
|
||||
str += ","
|
||||
}
|
||||
|
||||
str += fmt.Sprintf("%v", item)
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
func New(serverURL string) *Client {
|
||||
return &Client{
|
||||
serverURL: serverURL,
|
||||
http: &http.Client{},
|
||||
}
|
||||
}
|
25
internal/client/get_agent.go
Normal file
25
internal/client/get_agent.go
Normal file
@ -0,0 +1,25 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (c *Client) GetAgent(ctx context.Context, agentID datastore.AgentID) (*datastore.Agent, error) {
|
||||
response := withResponse[struct{ Agent *datastore.Agent }]()
|
||||
|
||||
path := fmt.Sprintf("/api/v1/agents/%d", agentID)
|
||||
|
||||
if err := c.apiGet(ctx, path, &response); err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
if response.Error != nil {
|
||||
return nil, errors.WithStack(response.Error)
|
||||
}
|
||||
|
||||
return response.Data.Agent, nil
|
||||
}
|
31
internal/client/get_agent_specs.go
Normal file
31
internal/client/get_agent_specs.go
Normal file
@ -0,0 +1,31 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
||||
"forge.cadoles.com/Cadoles/emissary/internal/spec"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (c *Client) GetAgentSpecs(ctx context.Context, agentID datastore.AgentID) ([]spec.Spec, error) {
|
||||
response := withResponse[struct{ Specs []*datastore.Spec }]()
|
||||
|
||||
path := fmt.Sprintf("/api/v1/agents/%d/specs", agentID)
|
||||
|
||||
if err := c.apiGet(ctx, path, &response); err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
if response.Error != nil {
|
||||
return nil, errors.WithStack(response.Error)
|
||||
}
|
||||
|
||||
specs := make([]spec.Spec, 0, len(response.Data.Specs))
|
||||
for _, s := range response.Data.Specs {
|
||||
specs = append(specs, spec.Spec(s))
|
||||
}
|
||||
|
||||
return specs, nil
|
||||
}
|
88
internal/client/query_agents.go
Normal file
88
internal/client/query_agents.go
Normal file
@ -0,0 +1,88 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type QueryAgentsOptionFunc func(*QueryAgentsOptions)
|
||||
|
||||
type QueryAgentsOptions struct {
|
||||
Limit *int
|
||||
Offset *int
|
||||
RemoteIDs []string
|
||||
IDs []datastore.AgentID
|
||||
Statuses []datastore.AgentStatus
|
||||
}
|
||||
|
||||
func WithQueryAgentsLimit(limit int) QueryAgentsOptionFunc {
|
||||
return func(opts *QueryAgentsOptions) {
|
||||
opts.Limit = &limit
|
||||
}
|
||||
}
|
||||
|
||||
func WithQueryAgentsOffset(offset int) QueryAgentsOptionFunc {
|
||||
return func(opts *QueryAgentsOptions) {
|
||||
opts.Offset = &offset
|
||||
}
|
||||
}
|
||||
|
||||
func WithQueryAgentsRemoteID(remoteIDs ...string) QueryAgentsOptionFunc {
|
||||
return func(opts *QueryAgentsOptions) {
|
||||
opts.RemoteIDs = remoteIDs
|
||||
}
|
||||
}
|
||||
|
||||
func WithQueryAgentsID(ids ...datastore.AgentID) QueryAgentsOptionFunc {
|
||||
return func(opts *QueryAgentsOptions) {
|
||||
opts.IDs = ids
|
||||
}
|
||||
}
|
||||
|
||||
func WithQueryAgentsStatus(statuses ...datastore.AgentStatus) QueryAgentsOptionFunc {
|
||||
return func(opts *QueryAgentsOptions) {
|
||||
opts.Statuses = statuses
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) QueryAgents(ctx context.Context, funcs ...QueryAgentsOptionFunc) ([]*datastore.Agent, int, error) {
|
||||
options := &QueryAgentsOptions{}
|
||||
for _, fn := range funcs {
|
||||
fn(options)
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
|
||||
if options.IDs != nil && len(options.IDs) > 0 {
|
||||
query.Set("ids", joinSlice(options.IDs))
|
||||
}
|
||||
|
||||
if options.RemoteIDs != nil && len(options.RemoteIDs) > 0 {
|
||||
query.Set("remote_ids", joinSlice(options.RemoteIDs))
|
||||
}
|
||||
|
||||
if options.Statuses != nil && len(options.RemoteIDs) > 0 {
|
||||
query.Set("statuses", joinSlice(options.Statuses))
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/api/v1/agents?%s", query.Encode())
|
||||
|
||||
response := withResponse[struct {
|
||||
Agents []*datastore.Agent
|
||||
Total int
|
||||
}]()
|
||||
|
||||
if err := c.apiGet(ctx, path, &response); err != nil {
|
||||
return nil, 0, errors.WithStack(err)
|
||||
}
|
||||
|
||||
if response.Error != nil {
|
||||
return nil, 0, errors.WithStack(response.Error)
|
||||
}
|
||||
|
||||
return response.Data.Agents, response.Data.Total, nil
|
||||
}
|
30
internal/client/register_agent.go
Normal file
30
internal/client/register_agent.go
Normal file
@ -0,0 +1,30 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (c *Client) RegisterAgent(ctx context.Context, remoteID string) (*datastore.Agent, error) {
|
||||
payload := struct {
|
||||
RemoteID string
|
||||
}{
|
||||
RemoteID: remoteID,
|
||||
}
|
||||
|
||||
response := withResponse[struct {
|
||||
Agent *datastore.Agent
|
||||
}]()
|
||||
|
||||
if err := c.apiPost(ctx, "/api/v1/register", payload, &response); err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
if response.Error != nil {
|
||||
return nil, errors.WithStack(response.Error)
|
||||
}
|
||||
|
||||
return response.Data.Agent, nil
|
||||
}
|
Reference in New Issue
Block a user