edc50c7510
Size Import Parts * parts * think * workingpart * textout
196 lines
4.3 KiB
Go
196 lines
4.3 KiB
Go
package objectstore
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"queryorchestration/internal/client"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type BucketLocation string
|
|
type BucketKey struct {
|
|
ClientID string
|
|
Location BucketLocation
|
|
CreatedAt time.Time
|
|
Part *uint16
|
|
EntityID uuid.UUID
|
|
FileType *string
|
|
}
|
|
|
|
const (
|
|
Import BucketLocation = "import"
|
|
TextTextract BucketLocation = "text/textract"
|
|
TextOut BucketLocation = "text/out"
|
|
Export BucketLocation = "export"
|
|
)
|
|
|
|
var PartExclusions = []BucketLocation{
|
|
Export,
|
|
}
|
|
|
|
const TimeFormat = "2006-01-02T150405Z"
|
|
const DateFormat = "20060102"
|
|
|
|
func (c *BucketKey) String() string {
|
|
return fmt.Sprintf("%s/%s", c.Prefix(), c.Filename())
|
|
}
|
|
|
|
func (c *BucketKey) Prefix() string {
|
|
dateStr := c.CreatedAt.Format(DateFormat)
|
|
|
|
partStr := ""
|
|
isExcluded := false
|
|
for _, location := range PartExclusions {
|
|
if location == c.Location {
|
|
isExcluded = true
|
|
break
|
|
}
|
|
}
|
|
if !isExcluded {
|
|
if c.Part == nil {
|
|
part := uint16(0)
|
|
c.Part = &part
|
|
}
|
|
partStr = fmt.Sprintf("/%d", *c.Part)
|
|
}
|
|
|
|
return fmt.Sprintf("%s/%s/%s%s", c.ClientID, c.Location, dateStr, partStr)
|
|
}
|
|
|
|
func (c *BucketKey) Filename() string {
|
|
location := strings.ReplaceAll(string(c.Location), "/", "-")
|
|
name := fmt.Sprintf("%s_%s_%s_%s", c.CreatedAt.Format(TimeFormat), c.ClientID, location, c.EntityID)
|
|
if c.FileType != nil {
|
|
name = fmt.Sprintf("%s.%s", name, *c.FileType)
|
|
}
|
|
|
|
return name
|
|
}
|
|
|
|
func (c *BucketKey) parseFilename(name string) error {
|
|
parts := strings.Split(name, ".")
|
|
if len(parts) != 1 && len(parts) != 2 {
|
|
return errors.New("incorrect amount of dots")
|
|
} else if len(parts) == 2 {
|
|
c.parseFileType(parts[1])
|
|
}
|
|
|
|
metadata := strings.Split(parts[0], "_")
|
|
if len(metadata) != 4 {
|
|
return errors.New("incorrect amount of metadata elements")
|
|
}
|
|
|
|
err := c.parseTime(metadata[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = c.parseClientID(metadata[1])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = c.parseLocation(metadata[2])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = c.parseEntityID(metadata[3])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
func (c *BucketKey) parseFileType(name string) {
|
|
c.FileType = &name
|
|
}
|
|
func (c *BucketKey) parseTime(name string) error {
|
|
parsedTime, err := time.Parse(TimeFormat, name)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse date: %w", err)
|
|
}
|
|
c.CreatedAt = parsedTime
|
|
|
|
return nil
|
|
}
|
|
func (c *BucketKey) parseClientID(name string) error {
|
|
c.ClientID = name
|
|
pattern := fmt.Sprintf(`^(%s)$`, client.CLIENT_ID_REGEX)
|
|
reg := regexp.MustCompile(pattern)
|
|
matches := reg.FindStringSubmatch(c.ClientID)
|
|
if len(matches) != 2 {
|
|
return errors.New("invalid client id")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
func (c *BucketKey) parseLocation(name string) error {
|
|
c.Location = BucketLocation(strings.ReplaceAll(name, "-", "/"))
|
|
pattern := fmt.Sprintf(`^(%s|%s|%s|%s)$`, Import, TextTextract, TextOut, Export)
|
|
reg := regexp.MustCompile(pattern)
|
|
matches := reg.FindStringSubmatch(string(c.Location))
|
|
if len(matches) != 2 {
|
|
return errors.New("invalid location")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
func (c *BucketKey) parseEntityID(name string) error {
|
|
entityId, err := uuid.Parse(name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.EntityID = entityId
|
|
|
|
return nil
|
|
}
|
|
|
|
func ParseBucketKey(key string) (BucketKey, error) {
|
|
locations := "[a-z]+/?[a-z]+?"
|
|
pattern := fmt.Sprintf(`^(%s)/(%s)/([0-9]{8})/(.+)$`, client.CLIENT_ID_REGEX, locations)
|
|
reg := regexp.MustCompile(pattern)
|
|
matches := reg.FindStringSubmatch(key)
|
|
if len(matches) != 5 {
|
|
return BucketKey{}, errors.New("does not match expectation")
|
|
}
|
|
|
|
formattedKey := BucketKey{}
|
|
|
|
filename := `.+`
|
|
reg = regexp.MustCompile(fmt.Sprintf(`^([0-9]+)/(%s)$`, filename))
|
|
subMatches := reg.FindStringSubmatch(matches[4])
|
|
if len(subMatches) == 3 {
|
|
part, err := strconv.ParseUint(subMatches[1], 10, 8)
|
|
if err != nil {
|
|
return BucketKey{}, err
|
|
}
|
|
safePart := uint16(part)
|
|
formattedKey.Part = &safePart
|
|
|
|
err = formattedKey.parseFilename(subMatches[2])
|
|
if err != nil {
|
|
return BucketKey{}, err
|
|
}
|
|
} else {
|
|
reg = regexp.MustCompile(fmt.Sprintf(`^(%s)$`, filename))
|
|
subMatches := reg.FindStringSubmatch(matches[4])
|
|
if len(subMatches) != 2 {
|
|
return BucketKey{}, errors.New("does not match expectation")
|
|
}
|
|
|
|
err := formattedKey.parseFilename(subMatches[1])
|
|
if err != nil {
|
|
return BucketKey{}, err
|
|
}
|
|
}
|
|
|
|
return formattedKey, nil
|
|
}
|