146 lines
4 KiB
Go
146 lines
4 KiB
Go
package redriver
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"errors"
|
|
"fmt"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/aws/aws-sdk-go-v2/aws"
|
|
"github.com/aws/aws-sdk-go-v2/aws/arn"
|
|
"github.com/aws/aws-sdk-go-v2/service/sqs/types"
|
|
)
|
|
|
|
func ARNFromURL(qURL string) (arn.ARN, error) {
|
|
a := arn.ARN{}
|
|
u, err := url.Parse(qURL)
|
|
if err != nil {
|
|
return a, fmt.Errorf("queue URL %q is not a valid URL: %w", qURL, err)
|
|
}
|
|
path := u.EscapedPath()
|
|
pathParts := strings.Split(strings.Trim(path, "/"), "/")
|
|
if len(pathParts) != 2 {
|
|
return a, fmt.Errorf("queue path %q does not have exactly 2 parts", path)
|
|
}
|
|
hostParts := strings.Split(u.Host, ".")
|
|
if len(hostParts) != 4 { // <service>.<region>.amazonaws.com
|
|
return a, fmt.Errorf("queue host %q does not have exactly 4 parts", u.Host)
|
|
}
|
|
|
|
a = arn.ARN{
|
|
Partition: "aws",
|
|
Service: hostParts[0],
|
|
Region: hostParts[1],
|
|
AccountID: pathParts[0],
|
|
Resource: pathParts[1],
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
func NameFromURL(qURL string) (name string, err error) {
|
|
u, err := url.Parse(qURL)
|
|
if err != nil {
|
|
return "", fmt.Errorf("queue URL %q is not a valid URL: %w", qURL, err)
|
|
}
|
|
path := u.EscapedPath()
|
|
parts := strings.Split(strings.Trim(path, "/"), "/")
|
|
if len(parts) != 2 {
|
|
return "", fmt.Errorf("queue path %q does not have exactly 2 parts", path)
|
|
}
|
|
return parts[1], nil
|
|
}
|
|
|
|
func NameFromARN(qARN arn.ARN) string {
|
|
return qARN.Resource
|
|
}
|
|
|
|
func NameFromARNString(qARN string) (name string, err error) {
|
|
a, err := arn.Parse(qARN)
|
|
if err != nil {
|
|
return "", fmt.Errorf("queue \"ARN\" %q is not an valid ARN", qARN)
|
|
}
|
|
return NameFromARN(a), nil
|
|
}
|
|
|
|
func URLFromARNString(qARN string) (name string, err error) {
|
|
a, err := arn.Parse(qARN)
|
|
if err != nil {
|
|
return "", fmt.Errorf("queue \"ARN\" %q is not an valid ARN", qARN)
|
|
}
|
|
return URLFromARN(a)
|
|
}
|
|
|
|
func URLFromARN(qARN arn.ARN) (name string, err error) {
|
|
path, err := url.JoinPath("/", qARN.AccountID, qARN.Resource)
|
|
if err != nil {
|
|
return "", fmt.Errorf("incorrect queue ARN: %w", err)
|
|
}
|
|
u := url.URL{
|
|
Scheme: "https",
|
|
Host: fmt.Sprintf("%s.%s.%s", qARN.Service, qARN.Region, "amazonaws.com"),
|
|
Path: path,
|
|
}
|
|
return u.String(), nil
|
|
}
|
|
|
|
// MessageAttributeValuesFromJSONable attempts to convert a plain JSON to Message.MessageAttributes field value.
|
|
//
|
|
// FIXME we should not be storing those maps in JSONable form anyway, because that loses information.
|
|
func MessageAttributeValuesFromJSONable(in map[string]any) (map[string]types.MessageAttributeValue, error) {
|
|
m := make(map[string]types.MessageAttributeValue, len(in))
|
|
for k, v := range in {
|
|
mav := types.MessageAttributeValue{StringValue: aws.String(fmt.Sprint(v))}
|
|
switch v.(type) {
|
|
case int:
|
|
mav.DataType = aws.String("Number")
|
|
case string:
|
|
mav.DataType = aws.String("String")
|
|
default:
|
|
return nil, fmt.Errorf("message attributes contain a non-convertible value: %T", v)
|
|
}
|
|
m[k] = mav
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
func JSONableFromMessageAttributeValues(m map[string]types.MessageAttributeValue) (map[string]any, error) {
|
|
j := make(map[string]any)
|
|
for name, attr := range m {
|
|
if attr.DataType == nil {
|
|
return nil, errors.New("empty DataType on message attribute value")
|
|
}
|
|
switch t := *attr.DataType; t {
|
|
case "String":
|
|
if attr.StringValue == nil {
|
|
return nil, fmt.Errorf("nil string value on string field %q", name)
|
|
}
|
|
j[name] = *attr.StringValue
|
|
case "Number":
|
|
if attr.StringValue == nil {
|
|
return nil, fmt.Errorf("nil string value on number field %q", name)
|
|
}
|
|
n, err := strconv.Atoi(*attr.StringValue)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid numeric string on number field %q", name)
|
|
}
|
|
j[name] = n
|
|
case "Binary":
|
|
if attr.BinaryValue == nil {
|
|
j[name] = nil
|
|
continue
|
|
}
|
|
in := attr.BinaryValue
|
|
out := make([]byte, base64.StdEncoding.DecodedLen(len(in)))
|
|
_, err := base64.StdEncoding.Decode(out, in)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed decoding binary field %q: %w", name, err)
|
|
}
|
|
j[name] = out
|
|
default:
|
|
return nil, fmt.Errorf("unimplemented DataType %q", t)
|
|
}
|
|
}
|
|
return j, nil
|
|
}
|