Add URL column to CSV export for items (#141)

* feat: Add URL column to CSV export for items

Enhanced the CSV export functionality to include a URL field for each item. This change required updating the export logic to generate and include item URLs based on the request's referer header.

* chore: add URL configuration to V1Controller for dynamic URL handling in item export
This commit is contained in:
Ryan Sheppard
2024-07-21 08:29:46 -05:00
committed by GitHub
parent 2d1016d362
commit 01f54aeb52
6 changed files with 49 additions and 4 deletions

View File

@@ -57,6 +57,12 @@ func WithSecureCookies(secure bool) func(*V1Controller) {
}
}
func WithURL(url string) func(*V1Controller) {
return func(ctrl *V1Controller) {
ctrl.url = url
}
}
type V1Controller struct {
cookieSecure bool
repo *repo.AllRepos
@@ -65,6 +71,7 @@ type V1Controller struct {
isDemo bool
allowRegistration bool
bus *eventbus.EventBus
url string
}
type (

View File

@@ -6,6 +6,7 @@ import (
"errors"
"math/big"
"net/http"
"net/url"
"strings"
"github.com/google/uuid"
@@ -333,7 +334,7 @@ func (ctrl *V1Controller) HandleItemsExport() errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
ctx := services.NewContext(r.Context())
csvData, err := ctrl.svc.Items.ExportCSV(r.Context(), ctx.GID)
csvData, err := ctrl.svc.Items.ExportCSV(r.Context(), ctx.GID, getHBURL(r.Header.Get("Referer"), ctrl.url))
if err != nil {
log.Err(err).Msg("failed to export items")
return validate.NewRequestError(err, http.StatusInternalServerError)
@@ -347,3 +348,26 @@ func (ctrl *V1Controller) HandleItemsExport() errchain.HandlerFunc {
return writer.WriteAll(csvData)
}
}
func getHBURL(refererHeader, fallback string) (hbURL string) {
hbURL = refererHeader
if hbURL == "" {
hbURL = fallback
}
return stripPathFromURL(hbURL)
}
// stripPathFromURL removes the path from a URL.
// ex. https://example.com/tools -> https://example.com
func stripPathFromURL(rawURL string) string {
parsedURL, err := url.Parse(rawURL)
if err != nil {
log.Err(err).Msg("failed to parse URL")
return ""
}
strippedURL := url.URL{Scheme: parsedURL.Scheme, Host: parsedURL.Host}
return strippedURL.String()
}

View File

@@ -3,6 +3,7 @@ package main
import (
"embed"
"errors"
"fmt"
"io"
"mime"
"net/http"
@@ -54,6 +55,7 @@ func (a *app) mountRoutes(r *chi.Mux, chain *errchain.ErrChain, repos *repo.AllR
v1.WithMaxUploadSize(a.conf.Web.MaxUploadSize),
v1.WithRegistration(a.conf.Options.AllowRegistration),
v1.WithDemoStatus(a.conf.Demo), // Disable Password Change in Demo Mode
v1.WithURL(fmt.Sprintf("%s:%s", a.conf.Web.Host, a.conf.Web.Port)),
)
r.Route(prefix+"/v1", func(r chi.Router) {

View File

@@ -18,6 +18,7 @@ type ExportCSVRow struct {
LabelStr LabelString `csv:"HB.labels"`
AssetID repo.AssetID `csv:"HB.asset_id"`
Archived bool `csv:"HB.archived"`
URL string `csv:"HB.url"`
Name string `csv:"HB.name"`
Quantity int `csv:"HB.quantity"`

View File

@@ -153,7 +153,7 @@ func (s *IOSheet) Read(data io.Reader) error {
}
// ReadItems writes the sheet to a writer.
func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.UUID, repos *repo.AllRepos) error {
func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.UUID, repos *repo.AllRepos, hbURL string) error {
s.Rows = make([]ExportCSVRow, len(items))
extraHeaders := map[string]struct{}{}
@@ -178,6 +178,8 @@ func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.
labelString[i] = l.Name
}
url := generateItemURL(item, hbURL)
customFields := make([]ExportItemFields, len(item.Fields))
for i, f := range item.Fields {
@@ -201,6 +203,7 @@ func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.
Description: item.Description,
Insured: item.Insured,
Archived: item.Archived,
URL: url,
PurchasePrice: item.PurchasePrice,
PurchaseFrom: item.PurchaseFrom,
@@ -252,6 +255,14 @@ func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.
return nil
}
func generateItemURL(item repo.ItemOut, d string) string {
url := ""
if item.ID != uuid.Nil {
url = fmt.Sprintf("%s/item/%s", d, item.ID.String())
}
return url
}
// CSV writes the current sheet to a 2d array, for compatibility with TSV/CSV files.
func (s *IOSheet) CSV() ([][]string, error) {
memcsv := make([][]string, len(s.Rows)+1)

View File

@@ -329,7 +329,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, GID uuid.UUID, data io.Re
return finished, nil
}
func (svc *ItemService) ExportCSV(ctx context.Context, GID uuid.UUID) ([][]string, error) {
func (svc *ItemService) ExportCSV(ctx context.Context, GID uuid.UUID, hbURL string) ([][]string, error) {
items, err := svc.repo.Items.GetAll(ctx, GID)
if err != nil {
return nil, err
@@ -337,7 +337,7 @@ func (svc *ItemService) ExportCSV(ctx context.Context, GID uuid.UUID) ([][]strin
sheet := reporting.IOSheet{}
err = sheet.ReadItems(ctx, items, GID, svc.repo)
err = sheet.ReadItems(ctx, items, GID, svc.repo, hbURL)
if err != nil {
return nil, err
}