feat: initial commit
All checks were successful
Cadoles/bouncer/pipeline/head This commit looks good

This commit is contained in:
2023-04-24 20:52:12 +02:00
commit e66938f1d3
134 changed files with 8507 additions and 0 deletions

144
internal/client/client.go Normal file
View File

@ -0,0 +1,144 @@
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
defaultOpts Options
serverURL string
}
func (c *Client) apiGet(ctx context.Context, path string, result any, funcs ...OptionFunc) error {
if err := c.apiDo(ctx, http.MethodGet, path, nil, result, funcs...); err != nil {
return errors.WithStack(err)
}
return nil
}
func (c *Client) apiPost(ctx context.Context, path string, payload any, result any, funcs ...OptionFunc) error {
if err := c.apiDo(ctx, http.MethodPost, path, payload, result, funcs...); err != nil {
return errors.WithStack(err)
}
return nil
}
func (c *Client) apiPut(ctx context.Context, path string, payload any, result any, funcs ...OptionFunc) error {
if err := c.apiDo(ctx, http.MethodPut, path, payload, result, funcs...); err != nil {
return errors.WithStack(err)
}
return nil
}
func (c *Client) apiDelete(ctx context.Context, path string, payload any, result any, funcs ...OptionFunc) error {
if err := c.apiDo(ctx, http.MethodDelete, path, payload, result, funcs...); err != nil {
return errors.WithStack(err)
}
return nil
}
func (c *Client) apiDo(ctx context.Context, method string, path string, payload any, response any, funcs ...OptionFunc) error {
opts := c.defaultOptions()
for _, fn := range funcs {
fn(opts)
}
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)
}
for key, values := range opts.Headers {
for _, v := range values {
req.Header.Add(key, v)
}
}
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 (c *Client) defaultOptions() *Options {
return &Options{
Headers: c.defaultOpts.Headers,
}
}
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, funcs ...OptionFunc) *Client {
opts := Options{}
for _, fn := range funcs {
fn(&opts)
}
return &Client{
serverURL: serverURL,
http: &http.Client{},
defaultOpts: opts,
}
}

View File

@ -0,0 +1,32 @@
package client
import (
"context"
"fmt"
"forge.cadoles.com/cadoles/bouncer/internal/admin"
"forge.cadoles.com/cadoles/bouncer/internal/store"
"github.com/pkg/errors"
)
func (c *Client) CreateLayer(ctx context.Context, proxyName store.ProxyName, layerName store.LayerName, layerType store.LayerType, options store.LayerOptions, funcs ...OptionFunc) (*store.Layer, error) {
request := admin.CreateLayerRequest{
Name: string(layerName),
Type: string(layerType),
Options: options,
}
response := withResponse[admin.CreateLayerResponse]()
path := fmt.Sprintf("/api/v1/proxies/%s/layers", proxyName)
if err := c.apiPost(ctx, path, request, &response); err != nil {
return nil, errors.WithStack(err)
}
if response.Error != nil {
return nil, errors.WithStack(response.Error)
}
return response.Data.Layer, nil
}

View File

@ -0,0 +1,30 @@
package client
import (
"context"
"net/url"
"forge.cadoles.com/cadoles/bouncer/internal/admin"
"forge.cadoles.com/cadoles/bouncer/internal/store"
"github.com/pkg/errors"
)
func (c *Client) CreateProxy(ctx context.Context, name store.ProxyName, to *url.URL, from []string, funcs ...OptionFunc) (*store.Proxy, error) {
request := admin.CreateProxyRequest{
Name: string(name),
To: to.String(),
From: from,
}
response := withResponse[admin.CreateProxyResponse]()
if err := c.apiPost(ctx, "/api/v1/proxies", request, &response); err != nil {
return nil, errors.WithStack(err)
}
if response.Error != nil {
return nil, errors.WithStack(response.Error)
}
return response.Data.Proxy, nil
}

View File

@ -0,0 +1,26 @@
package client
import (
"context"
"fmt"
"forge.cadoles.com/cadoles/bouncer/internal/admin"
"forge.cadoles.com/cadoles/bouncer/internal/store"
"github.com/pkg/errors"
)
func (c *Client) DeleteLayer(ctx context.Context, proxyName store.ProxyName, layerName store.LayerName, funcs ...OptionFunc) (store.LayerName, error) {
response := withResponse[admin.DeleteLayerResponse]()
path := fmt.Sprintf("/api/v1/proxies/%s/layers/%s", proxyName, layerName)
if err := c.apiDelete(ctx, path, nil, &response, funcs...); err != nil {
return "", errors.WithStack(err)
}
if response.Error != nil {
return "", errors.WithStack(response.Error)
}
return response.Data.LayerName, nil
}

View File

@ -0,0 +1,26 @@
package client
import (
"context"
"fmt"
"forge.cadoles.com/cadoles/bouncer/internal/admin"
"forge.cadoles.com/cadoles/bouncer/internal/store"
"github.com/pkg/errors"
)
func (c *Client) DeleteProxy(ctx context.Context, proxyName store.ProxyName, funcs ...OptionFunc) (store.ProxyName, error) {
response := withResponse[admin.DeleteProxyResponse]()
path := fmt.Sprintf("/api/v1/proxies/%s", proxyName)
if err := c.apiDelete(ctx, path, nil, &response, funcs...); err != nil {
return "", errors.WithStack(err)
}
if response.Error != nil {
return "", errors.WithStack(response.Error)
}
return response.Data.ProxyName, nil
}

View File

@ -0,0 +1,26 @@
package client
import (
"context"
"fmt"
"forge.cadoles.com/cadoles/bouncer/internal/admin"
"forge.cadoles.com/cadoles/bouncer/internal/store"
"github.com/pkg/errors"
)
func (c *Client) GetLayer(ctx context.Context, proxyName store.ProxyName, layerName store.LayerName, funcs ...OptionFunc) (*store.Layer, error) {
response := withResponse[admin.GetLayerResponse]()
path := fmt.Sprintf("/api/v1/proxies/%s/layers/%s", proxyName, layerName)
if err := c.apiGet(ctx, path, &response, funcs...); err != nil {
return nil, errors.WithStack(err)
}
if response.Error != nil {
return nil, errors.WithStack(response.Error)
}
return response.Data.Layer, nil
}

View File

@ -0,0 +1,26 @@
package client
import (
"context"
"fmt"
"forge.cadoles.com/cadoles/bouncer/internal/admin"
"forge.cadoles.com/cadoles/bouncer/internal/store"
"github.com/pkg/errors"
)
func (c *Client) GetProxy(ctx context.Context, proxyName store.ProxyName, funcs ...OptionFunc) (*store.Proxy, error) {
response := withResponse[admin.GetProxyResponse]()
path := fmt.Sprintf("/api/v1/proxies/%s", proxyName)
if err := c.apiGet(ctx, path, &response, funcs...); err != nil {
return nil, errors.WithStack(err)
}
if response.Error != nil {
return nil, errors.WithStack(response.Error)
}
return response.Data.Proxy, nil
}

View File

@ -0,0 +1,24 @@
package client
import "net/http"
type Options struct {
Headers http.Header
}
type OptionFunc func(*Options)
func WithToken(token string) OptionFunc {
return func(o *Options) {
if o.Headers == nil {
o.Headers = http.Header{}
}
o.Headers.Set("Authorization", "Bearer "+token)
}
}
func WithHeaders(headers http.Header) OptionFunc {
return func(o *Options) {
o.Headers = headers
}
}

View File

@ -0,0 +1,75 @@
package client
import (
"context"
"fmt"
"net/url"
"forge.cadoles.com/cadoles/bouncer/internal/admin"
"forge.cadoles.com/cadoles/bouncer/internal/store"
"github.com/pkg/errors"
)
type QueryLayerOptionFunc func(*QueryLayerOptions)
type QueryLayerOptions struct {
Options []OptionFunc
Offset *int
Limit *int
Names []store.LayerName
}
func WithQueryLayerOptions(funcs ...OptionFunc) QueryLayerOptionFunc {
return func(opts *QueryLayerOptions) {
opts.Options = funcs
}
}
func WithQueryLayerLimit(limit int) QueryLayerOptionFunc {
return func(opts *QueryLayerOptions) {
opts.Limit = &limit
}
}
func WithQueryLayerOffset(offset int) QueryLayerOptionFunc {
return func(opts *QueryLayerOptions) {
opts.Offset = &offset
}
}
func WithQueryLayerNames(names ...store.LayerName) QueryLayerOptionFunc {
return func(opts *QueryLayerOptions) {
opts.Names = names
}
}
func (c *Client) QueryLayer(ctx context.Context, proxyName store.ProxyName, funcs ...QueryLayerOptionFunc) ([]*store.LayerHeader, error) {
options := &QueryLayerOptions{}
for _, fn := range funcs {
fn(options)
}
query := url.Values{}
if options.Names != nil && len(options.Names) > 0 {
query.Set("names", joinSlice(options.Names))
}
path := fmt.Sprintf("/api/v1/proxies/%s/layers?%s", proxyName, query.Encode())
response := withResponse[admin.QueryLayerResponse]()
if options.Options == nil {
options.Options = make([]OptionFunc, 0)
}
if err := c.apiGet(ctx, path, &response, options.Options...); err != nil {
return nil, errors.WithStack(err)
}
if response.Error != nil {
return nil, errors.WithStack(response.Error)
}
return response.Data.Layers, nil
}

View File

@ -0,0 +1,75 @@
package client
import (
"context"
"fmt"
"net/url"
"forge.cadoles.com/cadoles/bouncer/internal/admin"
"forge.cadoles.com/cadoles/bouncer/internal/store"
"github.com/pkg/errors"
)
type QueryProxyOptionFunc func(*QueryProxyOptions)
type QueryProxyOptions struct {
Options []OptionFunc
Limit *int
Offset *int
Names []store.ProxyName
}
func WithQueryProxyOptions(funcs ...OptionFunc) QueryProxyOptionFunc {
return func(opts *QueryProxyOptions) {
opts.Options = funcs
}
}
func WithQueryProxyLimit(limit int) QueryProxyOptionFunc {
return func(opts *QueryProxyOptions) {
opts.Limit = &limit
}
}
func WithQueryProxyOffset(offset int) QueryProxyOptionFunc {
return func(opts *QueryProxyOptions) {
opts.Offset = &offset
}
}
func WithQueryProxyNames(names ...store.ProxyName) QueryProxyOptionFunc {
return func(opts *QueryProxyOptions) {
opts.Names = names
}
}
func (c *Client) QueryProxy(ctx context.Context, funcs ...QueryProxyOptionFunc) ([]*store.ProxyHeader, error) {
options := &QueryProxyOptions{}
for _, fn := range funcs {
fn(options)
}
query := url.Values{}
if options.Names != nil && len(options.Names) > 0 {
query.Set("names", joinSlice(options.Names))
}
path := fmt.Sprintf("/api/v1/proxies?%s", query.Encode())
response := withResponse[admin.QueryProxyResponse]()
if options.Options == nil {
options.Options = make([]OptionFunc, 0)
}
if err := c.apiGet(ctx, path, &response, options.Options...); err != nil {
return nil, errors.WithStack(err)
}
if response.Error != nil {
return nil, errors.WithStack(response.Error)
}
return response.Data.Proxies, nil
}

View File

@ -0,0 +1,28 @@
package client
import (
"context"
"fmt"
"forge.cadoles.com/cadoles/bouncer/internal/admin"
"forge.cadoles.com/cadoles/bouncer/internal/store"
"github.com/pkg/errors"
)
type UpdateLayerOptions = admin.UpdateLayerRequest
func (c *Client) UpdateLayer(ctx context.Context, proxyName store.ProxyName, layerName store.LayerName, opts *UpdateLayerOptions, funcs ...OptionFunc) (*store.Layer, error) {
response := withResponse[admin.UpdateLayerResponse]()
path := fmt.Sprintf("/api/v1/proxies/%s/layers/%s", proxyName, layerName)
if err := c.apiPut(ctx, path, opts, &response); err != nil {
return nil, errors.WithStack(err)
}
if response.Error != nil {
return nil, errors.WithStack(response.Error)
}
return response.Data.Layer, nil
}

View File

@ -0,0 +1,28 @@
package client
import (
"context"
"fmt"
"forge.cadoles.com/cadoles/bouncer/internal/admin"
"forge.cadoles.com/cadoles/bouncer/internal/store"
"github.com/pkg/errors"
)
type UpdateProxyOptions = admin.UpdateProxyRequest
func (c *Client) UpdateProxy(ctx context.Context, proxyName store.ProxyName, opts *UpdateProxyOptions, funcs ...OptionFunc) (*store.Proxy, error) {
response := withResponse[admin.UpdateProxyResponse]()
path := fmt.Sprintf("/api/v1/proxies/%s", proxyName)
if err := c.apiPut(ctx, path, opts, &response); err != nil {
return nil, errors.WithStack(err)
}
if response.Error != nil {
return nil, errors.WithStack(response.Error)
}
return response.Data.Proxy, nil
}