06/12/2020 - GO
Bu örnekte, iki strateji modeli uygulamasını inceleyeceğiz, ancak ikisi de aynı sonucu biraz farklı bir şekilde elde ediyor. Farklı bankalar için basitçe token oluştururlar.
Her iki strateji modeli de gerçek dünya senaryolarında geçerlidir, bu nedenle "yinelenen çözüm" mutlaka kötü bir tasarım olduğu anlamına gelmez. Burada, bu tür bir iş durumunu nasıl halledebileceğimizi göreceğiz. Ayrıca her iki modelin de güçlü ve zayıf yönleri vardır.
Her iki strateji modeli de istekteki ProviderName
parametresini kullanırlar. Verinin içeriğine göre:
├── main.go
└── obie
├── access_token.go
├── barclays
│ ├── config.go
│ └── provider.go
├── natwest
│ ├── config.go
│ └── provider.go
└── provider.go
package main
import (
"context"
"net/http"
"github.com/you/obie"
"github.com/you/obie/barclays"
"github.com/you/obie/natwest"
)
func main() {
barcPrv := barclays.NewProvider(barclays.Config{
TokenEndpoint: "https://barclays/token/endpoint",
ClientID: "barclays-client",
ClientSecret: "barclays-secret",
}, http.DefaultTransport)
natwPrv := natwest.NewProvider(natwest.Config{
TokenEndpoint: "https://natwest/token/endpoint",
ClientID: "natwest-client",
ClientSecret: "natwest-secret",
Scope: "natwest scope",
}, http.DefaultTransport)
provider := obie.NewProviderStrategy()
provider.Add(barclays.Name, barcPrv)
provider.Add(natwest.Name, natwPrv)
ctx := context.Background()
_, _ = accessToken(ctx, provider, obie.AccessTokenRequest{ProviderName: barclays.Name})
_, _ = accessToken(ctx, provider, obie.AccessTokenRequest{ProviderName: natwest.Name})
}
func accessToken(ctx context.Context, prv obie.Provider, req obie.AccessTokenRequest) (obie.AccessToken, error) {
return prv.AccessToken(ctx, req)
}
package obie
type AccessTokenRequest struct {
ProviderName Name
}
type AccessToken struct {
AccessToken string
RefreshToken string
TokenType string
Scope string
ExpiresIn int
}
package obie
import (
"context"
"fmt"
)
type Name string
type Provider interface {
AccessToken(context.Context, AccessTokenRequest) (AccessToken, error)
}
type ProviderStrategy struct {
providers map[Name]Provider
}
func NewProviderStrategy() *ProviderStrategy {
return &ProviderStrategy{
providers: make(map[Name]Provider),
}
}
func (p *ProviderStrategy) Add(name Name, prv Provider) {
p.providers[name] = prv
}
func (p *ProviderStrategy) AccessToken(ctx context.Context, req AccessTokenRequest) (AccessToken, error) {
if _, ok := p.providers[req.ProviderName]; !ok {
return AccessToken{}, fmt.Errorf("access token: unknown provider: %s", req.ProviderName)
}
return p.providers[req.ProviderName].AccessToken(ctx, req)
}
package barclays
type Config struct{
TokenEndpoint string
ClientID string
ClientSecret string
}
package barclays
import (
"bytes"
"context"
"fmt"
"net/http"
"net/http/httputil"
"net/url"
"github.com/you/obie"
)
var Name obie.Name = "Barclays"
type Provider struct {
config Config
transport http.RoundTripper
}
func NewProvider(conf Config, trans http.RoundTripper) Provider {
return Provider{
config: conf,
transport: trans,
}
}
func (p Provider) AccessToken(ctx context.Context, req obie.AccessTokenRequest) (obie.AccessToken, error) {
data := url.Values{}
data.Set("grant_type", "client_credentials")
data.Set("client_id", p.config.ClientID)
data.Set("client_secret", p.config.ClientSecret)
httpReq, _ := http.NewRequestWithContext(
ctx,
http.MethodPost,
p.config.TokenEndpoint,
bytes.NewReader([]byte(data.Encode())),
)
body, _ := httputil.DumpRequest(httpReq, true)
fmt.Println(string(body))
// Send the request and handle the response
// b.transport.RoundTrip(httpReq)
return obie.AccessToken{}, nil
}
package natwest
type Config struct{
TokenEndpoint string
ClientID string
ClientSecret string
Scope string
}
package natwest
import (
"bytes"
"context"
"fmt"
"net/http"
"net/http/httputil"
"net/url"
"github.com/you/obie"
)
var Name obie.Name = "Natwest"
type Provider struct {
config Config
transport http.RoundTripper
}
func NewProvider(conf Config, trans http.RoundTripper) Provider {
return Provider{
config: conf,
transport: trans,
}
}
func (p Provider) AccessToken(ctx context.Context, req obie.AccessTokenRequest) (obie.AccessToken, error) {
data := url.Values{}
data.Set("grant_type", "client_credentials")
data.Set("client_id", p.config.ClientID)
data.Set("client_secret", p.config.ClientSecret)
data.Set("scope", p.config.Scope)
httpReq, _ := http.NewRequestWithContext(
ctx,
http.MethodPost,
p.config.TokenEndpoint,
bytes.NewReader([]byte(data.Encode())),
)
body, _ := httputil.DumpRequest(httpReq, true)
fmt.Println(string(body))
// Send the request and handle the response
// b.transport.RoundTrip(httpReq)
return obie.AccessToken{}, nil
}
├── main.go
└── obie
├── access_token.go
├── client.go
├── config.go
└── refresh_token.go
package main
import (
"context"
"net/http"
"github.com/you/obie"
)
func main() {
barclays := obie.ProviderConfig{
TokenEndpoint: "https://barclays/token/endpoint",
ClientID: "barclays-client",
ClientSecret: "barclays-secret",
}
natwest := obie.ProviderConfig{
TokenEndpoint: "https://natwest/token/endpoint",
ClientID: "natwest-client",
ClientSecret: "natwest-secret",
}
config := &obie.Config{
Providers: map[obie.ProviderName]obie.ProviderConfig{
obie.ProviderName("barclays"): barclays,
obie.ProviderName("natwest"): natwest,
},
}
ctx := context.Background()
client := obie.NewClient(config, http.DefaultTransport)
_, _ = client.AccessToken(ctx, obie.AccessTokenRequest{ProviderName: "barclays"})
_, _ = client.RefreshToken(ctx, obie.RefreshTokenRequest{ProviderName: "natwest", RefreshToken: "ref-tok"})
}
package obie
type ProviderName string
type Config struct {
Providers map[ProviderName]ProviderConfig
}
type ProviderConfig struct {
TokenEndpoint string
ClientID string
ClientSecret string
}
package obie
import (
"context"
"net/http"
)
type Client interface {
AccessToken(context.Context, AccessTokenRequest) (AccessToken, error)
RefreshToken(context.Context, RefreshTokenRequest) (AccessToken, error)
}
type ClientStrategy struct {
config *Config
transport http.RoundTripper
}
func NewClient(config *Config, transport http.RoundTripper) ClientStrategy {
return ClientStrategy{
config: config,
transport: transport,
}
}
package obie
import (
"bytes"
"context"
"fmt"
"net/http"
"net/http/httputil"
"net/url"
)
type AccessTokenRequest struct {
ProviderName ProviderName
}
type AccessToken struct {
AccessToken string
RefreshToken string
TokenType string
Scope string
ExpiresIn int
}
func (c ClientStrategy) AccessToken(ctx context.Context, req AccessTokenRequest) (AccessToken, error) {
config := c.config.Providers[req.ProviderName]
data := url.Values{}
data.Set("grant_type", "client_credentials")
data.Set("client_id", config.ClientID)
data.Set("client_secret", config.ClientSecret)
httpReq, _ := http.NewRequestWithContext(
ctx,
http.MethodPost,
config.TokenEndpoint,
bytes.NewReader([]byte(data.Encode())),
)
httpReq.Header.Set("Cache-Control", "no-store")
httpReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
httpReq.Header.Set("Pragma", "no-cache")
body, _ := httputil.DumpRequest(httpReq, true)
fmt.Println(string(body))
// Send the request and handle the response
// c.transport.RoundTrip(httpReq)
return AccessToken{}, nil
}
package obie
import (
"bytes"
"context"
"fmt"
"net/http"
"net/http/httputil"
"net/url"
)
type RefreshTokenRequest struct {
ProviderName ProviderName
RefreshToken string
}
func (c ClientStrategy) RefreshToken(ctx context.Context, req RefreshTokenRequest) (AccessToken, error) {
config := c.config.Providers[req.ProviderName]
data := url.Values{}
data.Set("grant_type", "refresh_token")
data.Set("client_id", config.ClientID)
data.Set("client_secret", config.ClientSecret)
data.Set("refresh_token", req.RefreshToken)
httpReq, _ := http.NewRequestWithContext(
ctx,
http.MethodPost,
config.TokenEndpoint,
bytes.NewReader([]byte(data.Encode())),
)
httpReq.Header.Set("Cache-Control", "no-store")
httpReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
httpReq.Header.Set("Pragma", "no-cache")
body, _ := httputil.DumpRequest(httpReq, true)
fmt.Println(string(body))
// Send the request and handle the response
// c.transport.RoundTrip(httpReq)
return AccessToken{}, nil
}