🚚 Moved ResponseType into separated response package

This commit is contained in:
Maxim Lebedev 2024-05-06 18:24:25 +05:00
parent 70d6163099
commit 392f98c925
Signed by: toby3d
GPG Key ID: 1F14E25B7C119FC5
5 changed files with 183 additions and 192 deletions

View File

@ -5,6 +5,7 @@ import (
"testing"
"source.toby3d.me/toby3d/auth/internal/domain/grant"
"source.toby3d.me/toby3d/auth/internal/domain/response"
)
type Metadata struct {
@ -55,7 +56,7 @@ type Metadata struct {
// JSON array containing the response_type values supported. This
// differs from RFC8414 in that this parameter is OPTIONAL and that, if
// omitted, the default is code.
ResponseTypesSupported []ResponseType
ResponseTypesSupported []response.Type
// JSON array containing grant type values supported. If omitted, the
// default value differs from RFC8414 and is authorization_code.
@ -110,9 +111,9 @@ func TestMetadata(tb testing.TB) *Metadata {
ScopeRead,
ScopeUpdate,
},
ResponseTypesSupported: []ResponseType{
ResponseTypeCode,
ResponseTypeID,
ResponseTypesSupported: []response.Type{
response.Code,
response.ID,
},
GrantTypesSupported: []grant.Type{
grant.AuthorizationCode,

View File

@ -0,0 +1,96 @@
package response
import (
"errors"
"fmt"
"strconv"
"strings"
"source.toby3d.me/toby3d/auth/internal/common"
)
// NOTE(toby3d): Encapsulate enums in structs for extra compile-time safety:
// https://threedots.tech/post/safer-enums-in-go/#struct-based-enums
type Type struct {
responseType string
}
//nolint:gochecknoglobals // structs cannot be constants
var (
Und = Type{} // "und"
// ID indicates to the authorization server that this is an
// authentication request. If this parameter is missing, the
// authorization endpoint MUST default to id.
//
// Deprecated: Only accept response_type=code requests, and for
// backwards-compatible support, treat response_type=id requests as
// response_type=code requests:
// https://aaronparecki.com/2020/12/03/1/indieauth-2020#response-type
ID = Type{"id"} // "id"
// Code indicates to the authorization server that an
// authorization code should be returned as the response:
// https://indieauth.net/source/#authorization-request-li-1
Code = Type{"code"} // "code"
)
var ErrResponseTypeUnknown error = errors.New("unknown grant type")
// ParseType parse string as response type struct enum.
func ParseType(uid string) (Type, error) {
switch strings.ToLower(uid) {
case Code.responseType:
return Code, nil
case ID.responseType:
return ID, nil
}
return Und, fmt.Errorf("%w: %s", ErrResponseTypeUnknown, uid)
}
// UnmarshalForm implements custom unmarshler for form values.
func (t *Type) UnmarshalForm(src []byte) error {
responseType, err := ParseType(string(src))
if err != nil {
return fmt.Errorf("ResponseType: UnmarshalForm: %w", err)
}
*t = responseType
return nil
}
// UnmarshalJSON implements custom unmarshler for JSON.
func (t *Type) UnmarshalJSON(v []byte) error {
unquoted, err := strconv.Unquote(string(v))
if err != nil {
return fmt.Errorf("ResponseType: UnmarshalJSON: cannot unquote value '%s': %w", string(v), err)
}
responseType, err := ParseType(strings.ToLower(unquoted))
if err != nil {
return fmt.Errorf("ResponseType: UnmarshalJSON: cannot parse value '%s': %w", unquoted, err)
}
*t = responseType
return nil
}
func (t Type) MarshalJSON() ([]byte, error) {
return []byte(strconv.Quote(t.responseType)), nil
}
// String returns string representation of response type.
func (t Type) String() string {
if t == Und {
return common.Und
}
return t.responseType
}
func (t Type) GoString() string {
return "response.Type(" + t.String() + ")"
}

View File

@ -0,0 +1,82 @@
package response_test
import (
"testing"
"source.toby3d.me/toby3d/auth/internal/domain/response"
)
func TestParseType(t *testing.T) {
t.Parallel()
for name, tc := range map[string]struct {
input string
expect response.Type
}{
"id": {input: "id", expect: response.ID},
"code": {input: "code", expect: response.Code},
} {
t.Run(name, func(t *testing.T) {
t.Parallel()
actual, err := response.ParseType(tc.input)
if err != nil {
t.Fatalf("%+v", err)
}
if actual != tc.expect {
t.Errorf("ParseResponseType(%s) = %v, want %v", tc.input, actual, tc.expect)
}
})
}
}
func TestType_UnmarshalForm(t *testing.T) {
t.Parallel()
input := []byte("code")
actual := response.Und
if err := actual.UnmarshalForm(input); err != nil {
t.Fatalf("%+v", err)
}
if actual != response.Code {
t.Errorf("UnmarshalForm(%s) = %v, want %v", input, actual, response.Code)
}
}
func TestType_UnmarshalJSON(t *testing.T) {
t.Parallel()
input := []byte(`"code"`)
actual := response.Und
if err := actual.UnmarshalJSON(input); err != nil {
t.Fatalf("%+v", err)
}
if actual != response.Code {
t.Errorf("UnmarshalJSON(%s) = %v, want %v", input, actual, response.Code)
}
}
func TestType_String(t *testing.T) {
t.Parallel()
for name, tc := range map[string]struct {
input response.Type
expect string
}{
"id": {input: response.ID, expect: "id"},
"code": {input: response.Code, expect: "code"},
} {
t.Run(name, func(t *testing.T) {
t.Parallel()
if actual := tc.input.String(); actual != tc.expect {
t.Errorf("String() = %s, want %s", actual, tc.expect)
}
})
}
}

View File

@ -1,99 +0,0 @@
package domain
import (
"fmt"
"strconv"
"strings"
"source.toby3d.me/toby3d/auth/internal/common"
)
// NOTE(toby3d): Encapsulate enums in structs for extra compile-time safety:
// https://threedots.tech/post/safer-enums-in-go/#struct-based-enums
type ResponseType struct {
responseType string
}
//nolint:gochecknoglobals // structs cannot be constants
var (
ResponseTypeUnd = ResponseType{responseType: ""} // "und"
// ResponseTypeID indicates to the authorization server that this is an
// authentication request. If this parameter is missing, the
// authorization endpoint MUST default to id.
//
// Deprecated: Only accept response_type=code requests, and for
// backwards-compatible support, treat response_type=id requests as
// response_type=code requests:
// https://aaronparecki.com/2020/12/03/1/indieauth-2020#response-type
ResponseTypeID = ResponseType{responseType: "id"} // "id"
// ResponseTypeCode indicates to the authorization server that an
// authorization code should be returned as the response:
// https://indieauth.net/source/#authorization-request-li-1
ResponseTypeCode = ResponseType{responseType: "code"} // "code"
)
var ErrResponseTypeUnknown error = NewError(
ErrorCodeUnsupportedResponseType,
"unknown grant type",
"https://indieauth.net/source/#authorization-request",
)
// ParseResponseType parse string as response type struct enum.
func ParseResponseType(uid string) (ResponseType, error) {
switch strings.ToLower(uid) {
case ResponseTypeCode.responseType:
return ResponseTypeCode, nil
case ResponseTypeID.responseType:
return ResponseTypeID, nil
}
return ResponseTypeUnd, fmt.Errorf("%w: %s", ErrResponseTypeUnknown, uid)
}
// UnmarshalForm implements custom unmarshler for form values.
func (rt *ResponseType) UnmarshalForm(src []byte) error {
responseType, err := ParseResponseType(string(src))
if err != nil {
return fmt.Errorf("ResponseType: UnmarshalForm: %w", err)
}
*rt = responseType
return nil
}
// UnmarshalJSON implements custom unmarshler for JSON.
func (rt *ResponseType) UnmarshalJSON(v []byte) error {
uid, err := strconv.Unquote(string(v))
if err != nil {
return fmt.Errorf("ResponseType: UnmarshalJSON: %w", err)
}
responseType, err := ParseResponseType(uid)
if err != nil {
return fmt.Errorf("ResponseType: UnmarshalJSON: %w", err)
}
*rt = responseType
return nil
}
func (rt ResponseType) MarshalJSON() ([]byte, error) {
return []byte(strconv.Quote(rt.responseType)), nil
}
// String returns string representation of response type.
func (rt ResponseType) String() string {
if rt.responseType != "" {
return rt.responseType
}
return common.Und
}
func (rt ResponseType) GoString() string {
return "domain.ResponseType(" + rt.String() + ")"
}

View File

@ -1,89 +0,0 @@
//nolint:dupl
package domain_test
import (
"testing"
"source.toby3d.me/toby3d/auth/internal/domain"
)
func TestResponseTypeType(t *testing.T) {
t.Parallel()
for _, tc := range []struct {
in string
out domain.ResponseType
}{
{in: "id", out: domain.ResponseTypeID},
{in: "code", out: domain.ResponseTypeCode},
} {
tc := tc
t.Run(tc.in, func(t *testing.T) {
t.Parallel()
result, err := domain.ParseResponseType(tc.in)
if err != nil {
t.Fatalf("%+v", err)
}
if result != tc.out {
t.Errorf("ParseResponseType(%s) = %v, want %v", tc.in, result, tc.out)
}
})
}
}
func TestResponseType_UnmarshalForm(t *testing.T) {
t.Parallel()
input := []byte("code")
result := domain.ResponseTypeUnd
if err := result.UnmarshalForm(input); err != nil {
t.Fatalf("%+v", err)
}
if result != domain.ResponseTypeCode {
t.Errorf("UnmarshalForm(%s) = %v, want %v", input, result, domain.ResponseTypeCode)
}
}
func TestResponseType_UnmarshalJSON(t *testing.T) {
t.Parallel()
input := []byte(`"code"`)
result := domain.ResponseTypeUnd
if err := result.UnmarshalJSON(input); err != nil {
t.Fatalf("%+v", err)
}
if result != domain.ResponseTypeCode {
t.Errorf("UnmarshalJSON(%s) = %v, want %v", input, result, domain.ResponseTypeCode)
}
}
func TestResponseType_String(t *testing.T) {
t.Parallel()
for _, tc := range []struct {
name string
in domain.ResponseType
out string
}{
{name: "id", in: domain.ResponseTypeID, out: "id"},
{name: "code", in: domain.ResponseTypeCode, out: "code"},
} {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
result := tc.in.String()
if result != tc.out {
t.Errorf("String() = %v, want %v", result, tc.out)
}
})
}
}