♻️ Refactored client HTTP repository

This commit is contained in:
Maxim Lebedev 2021-09-24 04:01:28 +05:00
parent e552351bd5
commit 10cc589dc6
Signed by: toby3d
GPG Key ID: 1F14E25B7C119FC5
3 changed files with 189 additions and 6 deletions

View File

@ -0,0 +1,111 @@
package http
import (
"bytes"
"context"
"net/url"
"strings"
"github.com/tomnomnom/linkheader"
http "github.com/valyala/fasthttp"
"source.toby3d.me/website/oauth/internal/client"
"source.toby3d.me/website/oauth/internal/model"
"willnorris.com/go/microformats"
)
type httpClientRepository struct {
client *http.Client
}
func NewHTTPClientRepository(c *http.Client) client.Repository {
return &httpClientRepository{
client: c,
}
}
func (repo *httpClientRepository) Get(ctx context.Context, id string) (*model.Client, error) {
req := http.AcquireRequest()
defer http.ReleaseRequest(req)
req.Header.SetMethod(http.MethodGet)
req.SetRequestURI(id)
resp := http.AcquireResponse()
defer http.ReleaseResponse(resp)
if err := repo.client.Do(req, resp); err != nil {
return nil, err
}
client := new(model.Client)
client.ID = model.URL(id)
client.RedirectURI = make([]model.URL, 0)
for _, l := range linkheader.Parse(string(resp.Header.Peek(http.HeaderLink))) {
if !strings.Contains(l.Rel, "redirect_uri") {
continue
}
client.RedirectURI = append(client.RedirectURI, model.URL(l.URL))
}
u, err := url.Parse(id)
if err != nil {
return nil, err
}
data := microformats.Parse(bytes.NewReader(resp.Body()), u)
populateItems(client, data.Items)
populateRels(client, data.Rels)
return client, nil
}
func populateItems(c *model.Client, items []*microformats.Microformat) {
for _, item := range items {
if len(item.Type) == 0 && item.Type[0] != "h-app" && item.Type[0] != "h-x-app" {
continue
}
for key, property := range item.Properties {
if len(property) == 0 {
continue
}
switch key {
case "name":
for i := range property {
val, _ := property[i].(string)
c.Name = model.URL(val)
}
case "logo":
for i := range property {
switch val := property[i].(type) {
case string:
c.Logo = model.URL(val)
case map[string]string:
c.Logo = model.URL(val["value"])
}
}
case "url":
for i := range property {
val, _ := property[i].(string)
c.URL = model.URL(val)
}
}
}
}
}
func populateRels(c *model.Client, rels map[string][]string) {
for key, values := range rels {
if key != "redirect_uri" {
continue
}
for i := range values {
c.RedirectURI = append(c.RedirectURI, model.URL(values[i]))
}
}
}

View File

@ -0,0 +1,67 @@
package http_test
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/valyala/fasthttp"
"source.toby3d.me/website/oauth/internal/client/repository/http"
"source.toby3d.me/website/oauth/internal/common"
"source.toby3d.me/website/oauth/internal/model"
)
const testBody string = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Example App</title>
<link rel="redirect_uri" href="/redirect">
</head>
<body>
<div class="h-app">
<img src="/logo.png" class="u-logo">
<a href="/" class="u-url p-name">Example App</a>
</div>
</body>
</html>
`
func TestGet(t *testing.T) {
t.Parallel()
//nolint: exhaustivestruct
srv := &fasthttp.Server{
ReduceMemoryUsage: true,
GetOnly: true,
CloseOnShutdown: true,
Handler: func(ctx *fasthttp.RequestCtx) {
ctx.SuccessString(common.MIMETextHTML, testBody)
ctx.Response.Header.Set(fasthttp.HeaderLink, `<https://app.example.com/redirect>; rel="redirect_uri">`)
},
}
go func(srv *fasthttp.Server) {
assert.NoError(t, srv.ListenAndServe("127.0.0.1:2368"))
}(srv)
t.Cleanup(func() {
assert.NoError(t, srv.Shutdown())
})
result, err := http.NewHTTPClientRepository(new(fasthttp.Client)).Get(context.TODO(), "http://127.0.0.1:2368/")
require.NoError(t, err)
assert.Equal(t, &model.Client{
ID: "http://127.0.0.1:2368/",
Name: "Example App",
Logo: "http://127.0.0.1:2368/logo.png",
URL: "http://127.0.0.1:2368/",
RedirectURI: []model.URL{
"https://app.example.com/redirect",
"http://127.0.0.1:2368/redirect",
},
}, result)
}

View File

@ -1,11 +1,16 @@
package model
type Client struct {
ID string
URL string
Name string
Logo string
RedirectURI []string
ID URL
Name URL
Logo URL
URL URL
RedirectURI []URL
}
func (Client) Bucket() []byte { return []byte("clients") }
func NewClient() *Client {
c := new(Client)
c.RedirectURI = make([]URL, 0)
return c
}