Skip to content

Commit

Permalink
Merge pull request #28 from DEXPRO-Solutions-GmbH/feat/xml-rewrite
Browse files Browse the repository at this point in the history
Refactor client to use XML instead of JSON
  • Loading branch information
fabiante authored Feb 2, 2024
2 parents b396ac2 + 904b1c1 commit 87cba23
Show file tree
Hide file tree
Showing 11 changed files with 187 additions and 105 deletions.
2 changes: 1 addition & 1 deletion r_get_record.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type RecordAttachment struct {
}

func (c *StoreClient) GetRecord(ctx context.Context, id uuid.UUID) (*Record, error) {
req, err := c.newRequest(ctx)
req, err := c.newRequestXML(ctx)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion r_get_record_attachment.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
//
// This returns the number of bytes written and an error if any.
func (c *StoreClient) GetRecordAttachment(ctx context.Context, writer io.Writer, recordID, attachmentID uuid.UUID) (int64, error) {
req, err := c.newRequest(ctx)
req, err := newRequest(ctx, c.c, "")
if err != nil {
return 0, err
}
Expand Down
61 changes: 18 additions & 43 deletions r_get_store_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,35 @@ package easclient

import (
"context"
"time"
"encoding/xml"
)

type StoreStatus struct {
XMLName xml.Name `xml:"status"`
Registry struct {
AllRecords int `json:"allRecords"`
IndexedRecords int `json:"indexedRecords"`
AllAttachments int `json:"allAttachments"`
IndexedAttachments int `json:"indexedAttachments"`
} `json:"registry"`
Records struct {
All int `xml:"all"`
Indexed int `xml:"indexed"`
} `xml:"records"`
Attachments struct {
All int `xml:"all"`
Indexed int `xml:"indexed"`
} `xml:"attachments"`
} `xml:"registry"`
Index struct {
Documents int `json:"documents"`
IsCurrent bool `json:"isCurrent"`
HasDeletions bool `json:"hasDeletions"`
Records int `json:"records"`
Attachments int `json:"attachments"`
} `json:"index"`
Capacity struct {
Maximum int64 `json:"maximum"`
Utilized float64 `json:"utilized"`
GrowthRate float64 `json:"growthRate"`
ExpectedEnd time.Time `json:"expectedEnd"`
Lifetime int `json:"lifetime"`
} `json:"capacity"`
Periods []struct {
Start string `json:"start"`
End string `json:"end"`
Registry struct {
AllRecords int `json:"allRecords"`
IndexedRecords int `json:"indexedRecords"`
AllAttachments int `json:"allAttachments"`
IndexedAttachments int `json:"indexedAttachments"`
} `json:"registry"`
Index struct {
Records int `json:"records"`
Attachments int `json:"attachments"`
} `json:"index"`
Capacity struct {
Utilized float64 `json:"utilized"`
} `json:"capacity"`
} `json:"periods"`
Documents int `xml:"documents"`
IsCurrent bool `xml:"isCurrent"`
HasDeletions bool `xml:"hasDeletions"`
} `xml:"index"`
}

func (c *StoreClient) GetStoreStatus(ctx context.Context) (*StoreStatus, error) {
req, err := c.newRequest(ctx)
req, err := c.newRequestXML(ctx)
if err != nil {
return nil, err
}

type Res struct {
Status *StoreStatus `json:"status"`
}

var result Res
var result StoreStatus

req.SetResult(&result)
res, err := req.Get("/status")
Expand All @@ -67,5 +42,5 @@ func (c *StoreClient) GetStoreStatus(ctx context.Context) (*StoreStatus, error)
return nil, err
}

return result.Status, nil
return &result, nil
}
31 changes: 31 additions & 0 deletions r_get_store_status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package easclient_test

import (
"context"
"encoding/xml"
"testing"

"github.com/DEXPRO-Solutions-GmbH/easclient"
Expand All @@ -22,3 +23,33 @@ func TestStoreClient_GetStoreStatus(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, status)
}

func Test_UnmarshalStoreStatus(t *testing.T) {
respBody := `<?xml version="1.0" encoding="UTF-8"?>
<status xmlns="http://namespace.otris.de/2010/09/archive" xmlns:xlink="http://www.w3.org/1999/xlink">
<registry>
<records>
<all>34</all>
<indexed>34</indexed>
</records>
<attachments>
<all>4</all>
<indexed>4</indexed>
</attachments>
</registry>
<index>
<documents>38</documents>
<isCurrent>true</isCurrent>
<hasDeletions>false</hasDeletions>
</index>
</status>`

var resp easclient.StoreStatus

require.NoError(t, xml.Unmarshal([]byte(respBody), &resp))
require.Equal(t, 34, resp.Registry.Records.All)
require.Equal(t, 34, resp.Registry.Records.Indexed)
require.Equal(t, 38, resp.Index.Documents)
require.Equal(t, true, resp.Index.IsCurrent)
require.Equal(t, false, resp.Index.HasDeletions)
}
2 changes: 1 addition & 1 deletion r_put_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type PutStoreRequest struct {
}

func (c *ServerClient) PutStore(ctx context.Context, storeName string, request *PutStoreRequest) error {
req, err := newRequest(ctx, c.c)
req, err := newRequestJSON(ctx, c.c)
if err != nil {
return err
}
Expand Down
17 changes: 8 additions & 9 deletions r_store_post_record.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,23 @@ package easclient

import (
"context"
"encoding/xml"
"strings"

"github.com/google/uuid"
)

type PostRecordResponse struct {
Records []struct {
Id uuid.UUID `json:"id"`
Link struct {
Type string `json:"type"`
Title string `json:"title"`
Href string `json:"href"`
} `json:"link"`
} `json:"records"`
XMLName xml.Name `xml:"recordArchive"`
ID struct {
Value uuid.UUID `xml:",chardata"`
Type string `xml:"type,attr"`
Href string `xml:"href,attr"`
} `xml:"id"`
}

func (c *StoreClient) PostRecord(ctx context.Context, request *RecordRequest) (*PostRecordResponse, error) {
req, err := c.newRequest(ctx)
req, err := c.newRequestXML(ctx)
if err != nil {
return nil, err
}
Expand Down
5 changes: 1 addition & 4 deletions r_store_post_record_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,5 @@ func TestStoreClient_PostRecord(t *testing.T) {

require.NoError(t, err)
require.NotNil(t, res)

require.Len(t, res.Records, 1)
require.NoError(t, err)
require.NotEqual(t, uuid.Nil, res.Records[0].Id)
require.NotEqual(t, uuid.Nil, res.ID.Value)
}
89 changes: 61 additions & 28 deletions r_store_search.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package easclient

import (
"context"
"encoding/xml"
"fmt"
"net/url"
"strconv"
"time"

"github.com/google/uuid"
)
Expand Down Expand Up @@ -54,47 +56,78 @@ func SearchRequestFromURL(s string) (*SearchRequest, error) {
}

func (request SearchRequest) ToQuery() map[string]string {
return map[string]string{
q := map[string]string{
"query": request.Query,
"itemsPerPage": strconv.Itoa(request.ItemsPerPage),
"startIndex": strconv.Itoa(request.StartIndex),
"sort": request.Sort,
"sortOrder": request.SortOrder,
}

// delete zero values which would result in an invalid request
if q["itemsPerPage"] == "0" {
delete(q, "itemsPerPage")
}
if q["startIndex"] == "0" {
delete(q, "startIndex")
}

return q
}

type Link struct {
Type string `json:"type"`
Title string `json:"title"`
Href string `json:"href"`
Type string `xml:"type,attr"`
Href string `xml:"href,attr"`
}

type SearchResult struct {
// TODO: Re-add this field once we can confirm this is either always string or bool.
// Title string `json:"title"`
Score float64 `json:"score"`
Id uuid.UUID `json:"id"`
FileLink Link `json:"fileLink"`
ExplainLink Link `json:"explainLink"`
CheckVersionLink Link `json:"checkVersionLink"`
HistoryLink Link `json:"historyLink"`
VerifyLink Link `json:"verifyLink"`
HeaderFields HeaderFields `json:"headerFields"`
RecordFields RecordFields `json:"recordFields"`
type SearchResponse struct {
XMLName xml.Name `xml:"rss"`
Version string `xml:"version,attr"`
Channel *SearchResponseChannel `xml:"channel"`
}

type SearchResponse struct {
Query string `json:"query"`
TotalHits int `json:"totalHits"`
ItemsPerPage int `json:"itemsPerPage"`
StartIndex int `json:"startIndex"`
Topn int `json:"topn"`
EffectiveResults int `json:"effectiveResults"`
Result []*SearchResult `json:"result"`
type SearchResponseChannel struct {
Title string `xml:"title"`
Link string `xml:"link"`
Description string `xml:"description"`
TotalResults int `xml:"totalResults"` // TODO: Assert in unmarshal test
ItemsPerPage int `xml:"itemsPerPage"` // TODO: Assert in unmarshal test
StartIndex int `xml:"startIndex"` // TODO: Assert in unmarshal test
Query struct {
Role string `xml:"role,attr"`
SearchTerms string `xml:"searchTerms,attr"`
StartPage int `xml:"startPage,attr"`
} `xml:"Query"`
Topn int `xml:"topn"`
EffectiveResults int `xml:"effectiveResults"`
NextPage string `xml:"nextPage"`
Items []*SearchResponseItem `xml:"item"`
}

type SearchResponseItem struct {
Title string `xml:"title"`
Link string `xml:"link"`
Description string `xml:"description"`
Score float64 `xml:"score"`
ExplainLink Link `xml:"explainLink"`
VersionLink Link `xml:"versionLink"`
HistoryLink Link `xml:"historyLink"`
VerifyLink Link `xml:"verifyLink"`
DocumentType string `xml:"documentType"`
Fields []*RecordField `xml:"field"` // TODO: Assert and check in get attachment response if this is the correct way to handle recurring fields
MasterId uuid.UUID `xml:"masterId"`
ArchiveDateTime time.Time `xml:"archiveDateTime"`
ID uuid.UUID `xml:"id"`
Version string `xml:"version"`
ArchiverLogin string `xml:"archiverLogin"`
Archiver string `xml:"archiver"`
InitialArchiver string `xml:"initialArchiver"`
InitialArchiverLogin string `xml:"initialArchiverLogin"`
InitialArchiveDateTime time.Time `xml:"initialArchiveDateTime"`
}

// SearchQuery is similar to Search but expects a URL from which SearchRequest is parsed via SearchRequestFromURL.
func (c *StoreClient) SearchQuery(ctx context.Context, url string) (*SearchResponse, error) {
func (c *StoreClient) SearchQuery(ctx context.Context, url string) (*SearchResponseChannel, error) {
request, err := SearchRequestFromURL(url)
if err != nil {
return nil, fmt.Errorf("failed to parse search request: %w", err)
Expand All @@ -103,8 +136,8 @@ func (c *StoreClient) SearchQuery(ctx context.Context, url string) (*SearchRespo
return c.Search(ctx, request)
}

func (c *StoreClient) Search(ctx context.Context, request *SearchRequest) (*SearchResponse, error) {
req, err := c.newRequest(ctx)
func (c *StoreClient) Search(ctx context.Context, request *SearchRequest) (*SearchResponseChannel, error) {
req, err := c.newRequestXML(ctx)
if err != nil {
return nil, err
}
Expand All @@ -123,5 +156,5 @@ func (c *StoreClient) Search(ctx context.Context, request *SearchRequest) (*Sear
return nil, err
}

return &result, nil
return result.Channel, nil
}
26 changes: 21 additions & 5 deletions r_store_search_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,25 +28,41 @@ func TestStoreClient_Search(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, response)

assert.Equal(t, "Amazo*", response.Query)
assert.Greater(t, response.TotalHits, 0)
// assert search result in general
assert.Equal(t, "Amazo*", response.Query.SearchTerms)
assert.Greater(t, response.TotalResults, 0)
assert.Greater(t, response.EffectiveResults, 0)

// assert single hit
hit := response.Items[0]
require.NotNil(t, hit)
require.Greater(t, len(hit.Fields), 0)
require.Equal(t, "creditor", hit.Fields[0].Name)
require.Equal(t, "Amazon", hit.Fields[0].Value)
})

t.Run("returns results when using pagination details", func(t *testing.T) {
request := &easclient.SearchRequest{
Query: "Amazo*",
ItemsPerPage: 25,
StartIndex: 2500,
StartIndex: 1, // this requires at least 2 records to be present
}

response, err := eastest.DefaultClient().Search(ctx, request)
require.NoError(t, err)
require.NotNil(t, response)

assert.Equal(t, "Amazo*", response.Query)
assert.Greater(t, response.TotalHits, 0)
// assert search result in general
assert.Equal(t, "Amazo*", response.Query.SearchTerms)
assert.Greater(t, response.TotalResults, 0)
assert.Greater(t, response.EffectiveResults, 0)

// assert single hit
hit := response.Items[0]
require.NotNil(t, hit)
require.Greater(t, len(hit.Fields), 0)
require.Equal(t, "creditor", hit.Fields[0].Name)
require.Equal(t, "Amazon", hit.Fields[0].Value)
})
}

Expand Down
Loading

0 comments on commit 87cba23

Please sign in to comment.