From 4057c17487a6713adccad61da8a3a58a0863db37 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Thu, 16 May 2024 00:27:56 +0300 Subject: more on merge support --- internal/easyhttp/easyhttp.go | 19 ++++---- internal/server/handle/handle.go | 83 ++++++++++++++++++++++++++++---- internal/server/repository/repository.go | 56 ++++++++++++++++----- internal/types/entry.go | 5 ++ 4 files changed, 132 insertions(+), 31 deletions(-) (limited to 'internal') diff --git a/internal/easyhttp/easyhttp.go b/internal/easyhttp/easyhttp.go index aa405e6..7cd24ff 100644 --- a/internal/easyhttp/easyhttp.go +++ b/internal/easyhttp/easyhttp.go @@ -1,6 +1,7 @@ package easyhttp import ( + "fmt" "io" "net/http" "encoding/json" @@ -14,33 +15,31 @@ func Get(uri, apiKey string) ([]byte, error) { req, err := http.NewRequest("GET", uri, nil) if err != nil { - return bytes, err - } + return bytes, fmt.Errorf("%s: %w", uri, err) + } req.Header.Set("X-API-KEY", apiKey) resp, err := client.Do(req) if err != nil { - return bytes, err + return bytes, fmt.Errorf("%s: %w", uri, err) } defer resp.Body.Close() bytes, err = io.ReadAll(resp.Body) if err != nil { - return bytes, err + return bytes, fmt.Errorf("%s: %w", uri, err) } return bytes, nil } -func GetFromJson[T any](uri, apiKey string) (T, error) { - var data T - +// Get data from JSON +func GetData[T any](uri, apiKey string, data *T) error { bytes, err := Get(uri, apiKey) if err != nil { - return data, err + return err } - err = json.Unmarshal(bytes, &data) - return data, err + return json.Unmarshal(bytes, data) } diff --git a/internal/server/handle/handle.go b/internal/server/handle/handle.go index 011e31c..0a0a1bb 100644 --- a/internal/server/handle/handle.go +++ b/internal/server/handle/handle.go @@ -1,12 +1,13 @@ package handle import ( + "errors" "fmt" "io" - "log" "net/http" "os" "regexp" + "strings" "time" "codeberg.org/snonux/gos/internal" @@ -51,7 +52,7 @@ func List(w http.ResponseWriter, r *http.Request, dataDir string) error { return fmt.Errorf("expexted GET request") } - list, err := repository.New(dataDir).List() + list, err := repository.Instance(dataDir).List() if err != nil { return err } @@ -61,12 +62,12 @@ func List(w http.ResponseWriter, r *http.Request, dataDir string) error { } func Get(w http.ResponseWriter, r *http.Request, dataDir string) error { - path := r.URL.Query().Get("path") - if !getIDRe.MatchString(path) { - return fmt.Errorf("invalid path %s", path) + id := r.URL.Query().Get("id") + if !getIDRe.MatchString(id) { + return fmt.Errorf("invalid id %s", id) } - data, err := os.ReadFile(fmt.Sprintf("%s/%s", dataDir, path)) + data, err := os.ReadFile(fmt.Sprintf("%s/%s", dataDir, id)) if err != err { return err } @@ -76,11 +77,75 @@ func Get(w http.ResponseWriter, r *http.Request, dataDir string) error { } func Merge(w http.ResponseWriter, r *http.Request, conf server.ServerConfig) error { + var errs []error + for _, partner := range conf.Partners() { - uri := fmt.Sprintf("%s/list", partner) - data, err := easyhttp.Get(uri, conf.ApiKey) - log.Println(string(data), err) + if err := mergeFromPartner(conf, partner); err != nil { + errs = append(errs, err) + } + } + + err := combineErrors(errs) + if err != nil { + fmt.Fprint(w, err.Error()) + return err } + fmt.Fprint(w, "Okiedokie") return nil } + +func mergeFromPartner(conf server.ServerConfig, partner string) error { + var ( + errs []error + uri = fmt.Sprintf("%s/list", partner) + repo = repository.Instance(conf.DataDir) + pairs []repository.EntryPair + ) + + if err := easyhttp.GetData(uri, conf.ApiKey, &pairs); err != nil { + return err + } + + for _, pair := range pairs { + if repo.HasEntry(pair) { + continue + } + + var ( + entry types.Entry + uri = fmt.Sprintf("%s/get?id=%s", partner, pair.ID) + ) + + if err := easyhttp.GetData(uri, conf.ApiKey, &entry); err != nil { + errs = append(errs, err) + continue + } + + // In theory, this should never happen + if pair.ID != entry.ID { + errs = append(errs, fmt.Errorf("pair ID %s does not match entry id %s", pair.ID, entry.ID)) + continue + } + + repo.Merge(entry) + } + + return combineErrors(errs) +} + +func combineErrors(errs []error) error { + if len(errs) == 0 { + return nil + } + + var sb strings.Builder + for i, err := range errs { + if i > 0 { + sb.WriteString("; ") + } + sb.WriteString(err.Error()) + } + + return errors.New(sb.String()) +} diff --git a/internal/server/repository/repository.go b/internal/server/repository/repository.go index 7eb7122..4146526 100644 --- a/internal/server/repository/repository.go +++ b/internal/server/repository/repository.go @@ -11,23 +11,35 @@ import ( "codeberg.org/snonux/gos/internal/types" ) +var ( + instance Repository + once sync.Once +) + +// Contains an Entry ID and its checksumm, for the list and merge operations. +type EntryPair struct { + ID, Checksum string +} + type Repository struct { dataDir string entries map[string]types.Entry mu *sync.Mutex } -func New(dataDir string) Repository { - return Repository{ - dataDir: dataDir, - entries: make(map[string]types.Entry), - mu: &sync.Mutex{}, - } +func Instance(dataDir string) Repository { + once.Do(func() { + instance.dataDir = dataDir + instance.entries = make(map[string]types.Entry) + instance.mu = &sync.Mutex{} + }) + return instance } func (r Repository) store(entry types.Entry) { r.mu.Lock() defer r.mu.Unlock() + r.entries[entry.ID] = entry } @@ -60,17 +72,37 @@ func (r Repository) List() ([]byte, error) { return []byte{}, err } - type pair struct { - ID, Checksum string - } - - var pairs []pair + var pairs []EntryPair r.mu.Lock() defer r.mu.Unlock() for _, entry := range r.entries { - pairs = append(pairs, pair{entry.ID, entry.Checksum()}) + pairs = append(pairs, EntryPair{entry.ID, entry.Checksum()}) } return json.Marshal(pairs) } + +func (r Repository) HasEntry(pair EntryPair) bool { + r.mu.Lock() + defer r.mu.Unlock() + + ent, ok := r.entries[pair.ID] + if !ok || ent.Checksum() != pair.Checksum { + return false + } + return true +} + +func (r Repository) Merge(new types.Entry) { + r.mu.Lock() + defer r.mu.Unlock() + + old, ok := r.entries[new.ID] + if !ok { + r.entries[new.ID] = new + return + } + + r.entries[new.ID] = old.Update(new) +} diff --git a/internal/types/entry.go b/internal/types/entry.go index 067d849..fe0b4f5 100644 --- a/internal/types/entry.go +++ b/internal/types/entry.go @@ -50,6 +50,11 @@ func NewEntryFromFile(filePath string) (Entry, error) { return NewEntry(bytes) } +func (e Entry) Update(new Entry) Entry { + panic("not yet implemented") + //return e +} + func (e Entry) Serialize() ([]byte, error) { return json.Marshal(e) } -- cgit v1.2.3