From 3d5a93d45c0a28d7463d72dc8d76eef3366e59b5 Mon Sep 17 00:00:00 2001 From: Hugo Wetterberg Date: Wed, 17 Jun 2026 11:17:24 +0200 Subject: [PATCH 1/6] add SearchV1.GetFlatDocument endpoint for ELELOG-209 Fetch a document directly from the repository and return it in the flattened indexing representation, bypassing the eventual consistency of OpenSearch. Extract the shared repo-to-DocumentState assembly into newDocumentState, reused by the indexer enrichment path. --- go.mod | 2 +- go.sum | 4 +- index/common_test.go | 2 +- index/index.go | 3 -- index/index_worker.go | 53 +++++++++++++---------- index/search_api.go | 92 ++++++++++++++++++++++++++++++++++++++++ index/search_api_test.go | 70 ++++++++++++++++++++++++++++++ index/server.go | 1 + 8 files changed, 197 insertions(+), 30 deletions(-) diff --git a/go.mod b/go.mod index 94b9222..b6290ee 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/opensearch-project/opensearch-go/v2 v2.3.0 github.com/ory/dockertest/v3 v3.12.0 github.com/prometheus/client_golang v1.23.2 - github.com/ttab/elephant-api v0.23.0 + github.com/ttab/elephant-api v0.24.1-0.20260617083910-2e37458d4b02 github.com/ttab/elephantine v0.27.1 github.com/ttab/eltest v0.2.2 github.com/ttab/flerr v0.1.0 diff --git a/go.sum b/go.sum index 0109d82..604f5ae 100644 --- a/go.sum +++ b/go.sum @@ -277,8 +277,8 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tinylib/msgp v1.5.0 h1:GWnqAE54wmnlFazjq2+vgr736Akg58iiHImh+kPY2pc= github.com/tinylib/msgp v1.5.0/go.mod h1:cvjFkb4RiC8qSBOPMGPSzSAx47nAsfhLVTCZZNuHv5o= -github.com/ttab/elephant-api v0.23.0 h1:tuZ47+3gqqKOGVaFsWFo2JsEiwjFZgypEvE5s7a9hP0= -github.com/ttab/elephant-api v0.23.0/go.mod h1:4yLd0PwZRjsRj0Lb7QSQPaoYvz0zYZ7fALe1ANaYOhg= +github.com/ttab/elephant-api v0.24.1-0.20260617083910-2e37458d4b02 h1:HmKodM9ORkVoULvCSXuacdaPVlWITuzHKvoeGvf4UO4= +github.com/ttab/elephant-api v0.24.1-0.20260617083910-2e37458d4b02/go.mod h1:4yLd0PwZRjsRj0Lb7QSQPaoYvz0zYZ7fALe1ANaYOhg= github.com/ttab/elephantine v0.27.1 h1:kDX9KsPBT4r9DUN3iRqFxhnhA71t3nMTKJbmvCtat14= github.com/ttab/elephantine v0.27.1/go.mod h1:z6v4f7dEiAeOydoHAJ2HkJ6eAjTgvF8HBTOxE73NY1k= github.com/ttab/eltest v0.2.2 h1:T9+XZdLQJQciy7U/Q+TZskaxpnwdRJKEtaSz0699H+4= diff --git a/index/common_test.go b/index/common_test.go index 90a44db..4a8f457 100644 --- a/index/common_test.go +++ b/index/common_test.go @@ -90,7 +90,7 @@ func testingAPIServer( elephantine.LogKeyComponent, "schema-loader"), schemas) test.Must(t, err, "create schema loader") - metrics, err := index.NewMetrics(prometheus.DefaultRegisterer) + metrics, err := index.NewMetrics(reg) test.Must(t, err, "set up metrics") appExited := make(chan struct{}) diff --git a/index/index.go b/index/index.go index 2098a41..9d18155 100644 --- a/index/index.go +++ b/index/index.go @@ -14,7 +14,6 @@ import ( "github.com/jackc/pgx/v5/pgxpool" "github.com/opensearch-project/opensearch-go/v2" - "github.com/ttab/elephant-api/newsdoc" "github.com/ttab/elephant-api/repository" "github.com/ttab/elephant-index/internal" "github.com/ttab/elephant-index/postgres" @@ -197,8 +196,6 @@ type enrichJob struct { UUID string Operation int State *DocumentState - doc *newsdoc.Document - metadoc *newsdoc.Document fields map[string][]string } diff --git a/index/index_worker.go b/index/index_worker.go index 15e1924..414bfcd 100644 --- a/index/index_worker.go +++ b/index/index_worker.go @@ -125,12 +125,10 @@ func (iw *indexWorker) loop(ctx context.Context) { func (iw *indexWorker) enrich( job *enrichJob, ) (*DocumentState, error) { - state := DocumentState{ - Heads: make(map[string]Status), - } - if job.Operation == opDelete { - return &state, nil + return &DocumentState{ + Heads: make(map[string]Status), + }, nil } ctx, cancel := context.WithTimeout(job.ctx, 5*time.Second) @@ -145,12 +143,6 @@ func (iw *indexWorker) enrich( return nil, fmt.Errorf("get document: %w", err) } - job.doc = docRes.Document - - if docRes.Meta != nil { - job.metadoc = docRes.Meta.Document - } - metaRes, err := iw.idx.documents.GetMeta(ctx, &repository.GetMetaRequest{ Uuid: job.UUID, }) @@ -158,9 +150,28 @@ func (iw *indexWorker) enrich( return nil, fmt.Errorf("get document metadata: %w", err) } - state.CurrentVersion = metaRes.Meta.CurrentVersion - state.WorkflowState = metaRes.Meta.WorkflowState - state.WorkflowCheckpoint = metaRes.Meta.WorkflowCheckpoint + state, err := newDocumentState(docRes, metaRes) + if err != nil { + return nil, err + } + + return state, nil +} + +// newDocumentState assembles the indexable document state from a repository +// document and its metadata. +func newDocumentState( + docRes *repository.GetDocumentResponse, + metaRes *repository.GetMetaResponse, +) (*DocumentState, error) { + state := DocumentState{ + Heads: make(map[string]Status), + CurrentVersion: metaRes.Meta.CurrentVersion, + WorkflowState: metaRes.Meta.WorkflowState, + WorkflowCheckpoint: metaRes.Meta.WorkflowCheckpoint, + Creator: metaRes.Meta.CreatorUri, + Updater: metaRes.Meta.UpdaterUri, + } created, err := time.Parse(time.RFC3339, metaRes.Meta.Created) if err != nil { @@ -170,14 +181,12 @@ func (iw *indexWorker) enrich( modified, err := time.Parse(time.RFC3339, metaRes.Meta.Modified) if err != nil { - return nil, fmt.Errorf("parse document created time: %w", + return nil, fmt.Errorf("parse document modified time: %w", err) } state.Created = created - state.Creator = metaRes.Meta.CreatorUri state.Modified = modified - state.Updater = metaRes.Meta.UpdaterUri for _, v := range metaRes.Meta.Acl { state.ACL = append(state.ACL, ACLEntry{ @@ -193,21 +202,19 @@ func (iw *indexWorker) enrich( name, err) } - status := Status{ + state.Heads[name] = Status{ ID: v.Id, Version: v.Version, Creator: v.Creator, Created: created, Meta: v.Meta, } - - state.Heads[name] = status } - state.Document = newsdoc.DocumentFromRPC(job.doc) + state.Document = newsdoc.DocumentFromRPC(docRes.Document) - if job.metadoc != nil { - md := newsdoc.DocumentFromRPC(job.metadoc) + if docRes.Meta != nil { + md := newsdoc.DocumentFromRPC(docRes.Meta.Document) state.MetaDocument = &md } diff --git a/index/search_api.go b/index/search_api.go index b9dcd46..55c4701 100644 --- a/index/search_api.go +++ b/index/search_api.go @@ -47,6 +47,8 @@ func NewSearchServiceV1( percChanges *pg.FanOut[PercolatorUpdate], eventPercolated *pg.FanOut[EventPercolated], percDocs PercolatorDocumentGetter, + validator ValidatorSource, + languages LanguageOptions, ) *SearchServiceV1 { return &SearchServiceV1{ log: logger, @@ -57,6 +59,8 @@ func NewSearchServiceV1( percDocs: percDocs, percChanges: percChanges, eventPercolated: eventPercolated, + validator: validator, + languages: languages, subscriptions: sturdyc.New[userSub]( 5000, 5, 30*time.Minute, 10, sturdyc.WithEvictionInterval(10*time.Second), @@ -73,9 +77,97 @@ type SearchServiceV1 struct { percDocs PercolatorDocumentGetter percChanges *pg.FanOut[PercolatorUpdate] eventPercolated *pg.FanOut[EventPercolated] + validator ValidatorSource + languages LanguageOptions subscriptions *sturdyc.Client[userSub] } +// GetFlatDocument implements index.SearchV1. It fetches a document directly +// from the repository and returns it in the flattened representation used for +// indexing, bypassing the eventual consistency of OpenSearch. +func (s *SearchServiceV1) GetFlatDocument( + ctx context.Context, req *index.GetFlatDocumentRequest, +) (*index.GetFlatDocumentResponse, error) { + auth, err := RequireAnyScope(ctx, ScopeSearch, ScopeIndexAdmin) + if err != nil { + return nil, err + } + + if req.Uuid == "" { + return nil, twirp.RequiredArgumentError("uuid") + } + + // Forward the authentication header so that the repository applies the + // caller's read permissions. + authCtx, err := twirp.WithHTTPRequestHeaders(ctx, http.Header{ + "Authorization": []string{"Bearer " + auth.Token}, + }) + if err != nil { + return nil, twirp.InternalErrorf("invalid header handling: %w", err) + } + + docRes, err := s.documents.Get(authCtx, &repository.GetDocumentRequest{ + Uuid: req.Uuid, + Version: req.Version, + Status: req.Status, + MetaDocument: repository.GetMetaDoc_META_INCLUDE, + }) + if err != nil { + return nil, twirp.InternalErrorf( + "get document from repository: %w", err) + } + + metaRes, err := s.documents.GetMeta(authCtx, &repository.GetMetaRequest{ + Uuid: req.Uuid, + }) + if err != nil { + return nil, twirp.InternalErrorf( + "get document metadata from repository: %w", err) + } + + state, err := newDocumentState(docRes, metaRes) + if err != nil { + return nil, twirp.InternalErrorf("build document state: %w", err) + } + + if req.DocumentType != "" { + state.Document.Type = req.DocumentType + } + + languageCode := req.Language + if languageCode == "" { + languageCode = state.Document.Language + } + + // Use a per-request resolver as the cache it maintains isn't safe for + // concurrent use. + language, err := NewLanguageResolver(s.languages).GetLanguageInfo(languageCode) + if err != nil { + return nil, twirp.InvalidArgumentError("language", + fmt.Sprintf("could not resolve language %q: %v", + languageCode, err)) + } + + flat, err := BuildDocument( + s.validator.GetValidator(), state, GetIndexConfig(language), nil) + if err != nil { + return nil, twirp.InternalErrorf("flatten document: %w", err) + } + + res := index.GetFlatDocumentResponse{ + Fields: make(map[string]*index.FieldValuesV1), + Document: docRes.Document, + } + + for field, values := range flat.Values() { + res.Fields[field] = &index.FieldValuesV1{ + Values: values, + } + } + + return &res, nil +} + // EndSubscription implements index.SearchV1. func (s *SearchServiceV1) EndSubscription( _ context.Context, _ *index.EndSubscriptionRequest, diff --git a/index/search_api_test.go b/index/search_api_test.go index d0f4488..b79b398 100644 --- a/index/search_api_test.go +++ b/index/search_api_test.go @@ -1,15 +1,85 @@ package index_test import ( + "log/slog" + "path/filepath" "testing" "github.com/golang-jwt/jwt/v5" "github.com/ttab/elephant-api/index" + "github.com/ttab/elephant-api/repository" "github.com/ttab/elephant-index/internal" "github.com/ttab/elephantine" "github.com/ttab/elephantine/test" ) +func TestGetFlatDocument(t *testing.T) { + ctx := t.Context() + logger := slog.New(test.NewLogHandler(t, slog.LevelWarn)) + + tc := testingAPIServer(t, logger) + + documents := repository.NewDocumentsProtobufClient( + tc.Env.Repository.GetAPIEndpoint(), + tc.AuthenticatedClient(t, "doc_read", "doc_write", "eventlog_read")) + + search := index.NewSearchV1ProtobufClient(tc.IndexEndpoint, + tc.AuthenticatedClient(t, "doc_read", "search")) + + docDataDir := filepath.Join("..", "testdata", "documents") + + loadDocuments(t, documents, docDataDir, "russia_v1.json") + + const russiaUUID = "f5d2e4c5-01ba-4dae-9f09-a86701e06ecd" + + // The flattened document is fetched directly from the repository, so it's + // available without waiting for OpenSearch to catch up. + res, err := search.GetFlatDocument(ctx, &index.GetFlatDocumentRequest{ + Uuid: russiaUUID, + }) + test.Must(t, err, "get flattened document") + + test.Equal(t, russiaUUID, res.Document.Uuid, "returned document UUID") + + test.EqualDiff(t, + []string{"Rysslands ambassadör kallas upp"}, + flatFieldValues(res, "document.title"), + "flattened document title") + + test.EqualDiff(t, + []string{"core://newscoverage/" + russiaUUID}, + flatFieldValues(res, "document.uri"), + "flattened document URI") + + test.EqualDiff(t, []string{"1"}, + flatFieldValues(res, "current_version"), + "flattened current version") +} + +func TestGetFlatDocumentRequiresUUID(t *testing.T) { + ctx := t.Context() + logger := slog.New(test.NewLogHandler(t, slog.LevelWarn)) + + tc := testingAPIServer(t, logger) + + search := index.NewSearchV1ProtobufClient(tc.IndexEndpoint, + tc.AuthenticatedClient(t, "doc_read", "search")) + + _, err := search.GetFlatDocument(ctx, &index.GetFlatDocumentRequest{}) + test.MustNot(t, err, "reject request without a UUID") +} + +func flatFieldValues( + res *index.GetFlatDocumentResponse, name string, +) []string { + f := res.Fields[name] + if f == nil { + return nil + } + + return f.Values +} + func TestIndexPattern(t *testing.T) { test.Equal(t, "documents-foo-*-*", internal.IndexPattern("foo", &index.QueryRequestV1{}), diff --git a/index/server.go b/index/server.go index c739b68..38dcf86 100644 --- a/index/server.go +++ b/index/server.go @@ -94,6 +94,7 @@ func RunIndex(ctx context.Context, p Parameters) error { NewPostgresMappingSource(postgres.New(p.Database)), coord, p.AnonymousDocuments, coord.percolatorUpdate, coord.eventPercolated, percDocs, + p.Validator, p.Languages, ), twirp.WithServerJSONSkipDefaults(true), twirp.WithServerHooks(opts.Hooks), From 125a6068bcfc1dec8aa146eae04e8e543cdcd64d Mon Sep 17 00:00:00 2001 From: Hugo Wetterberg Date: Wed, 17 Jun 2026 11:54:05 +0200 Subject: [PATCH 2/6] drop type/language from GetFlatDocument, add stored flag The document type and language are derived from the document itself, so there's no need to specify them at request time. Add a stored flag that returns the flattened document as it is currently stored in the active index instead of converting the current repository version. --- go.mod | 13 +++- go.sum | 26 ++++++++ index/search_api.go | 133 ++++++++++++++++++++++++++++++++++----- index/search_api_test.go | 59 ++++++++++++++--- 4 files changed, 207 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index b6290ee..45faef0 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/opensearch-project/opensearch-go/v2 v2.3.0 github.com/ory/dockertest/v3 v3.12.0 github.com/prometheus/client_golang v1.23.2 - github.com/ttab/elephant-api v0.24.1-0.20260617083910-2e37458d4b02 + github.com/ttab/elephant-api v0.24.1-0.20260617093243-b6e87ecd0563 github.com/ttab/elephantine v0.27.1 github.com/ttab/eltest v0.2.2 github.com/ttab/flerr v0.1.0 @@ -64,17 +64,21 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.43.1 // indirect github.com/aws/smithy-go v1.27.0 // indirect github.com/aymerick/douceur v0.2.0 // indirect + github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/buger/jsonparser v1.2.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/containerd/continuity v0.4.5 // indirect github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/cli v29.0.0+incompatible // indirect github.com/docker/go-connections v0.6.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect + github.com/fatih/structtag v1.2.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/go-jose/go-jose/v4 v4.1.4 // indirect @@ -95,6 +99,7 @@ require ( github.com/hashicorp/vault/api v1.23.0 // indirect github.com/hashicorp/vault/api/auth/kubernetes v0.12.0 // indirect github.com/huandu/xstrings v1.5.0 // indirect + github.com/invopop/jsonschema v0.13.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect @@ -104,6 +109,7 @@ require ( github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/klauspost/crc32 v1.3.0 // indirect github.com/magefile/mage v1.17.2 // indirect + github.com/mailru/easyjson v0.9.2 // indirect github.com/minio/crc64nvme v1.1.1 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/minio/minio-go/v7 v7.0.97 // indirect @@ -125,14 +131,19 @@ require ( github.com/prometheus/common v0.68.0 // indirect github.com/prometheus/procfs v0.20.1 // indirect github.com/rs/xid v1.6.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect + github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/tinylib/msgp v1.5.0 // indirect + github.com/urfave/cli/v2 v2.27.7 // indirect + github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect + github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect go.opentelemetry.io/otel v1.38.0 // indirect diff --git a/go.sum b/go.sum index 604f5ae..3b6de11 100644 --- a/go.sum +++ b/go.sum @@ -71,8 +71,12 @@ github.com/aws/smithy-go v1.27.0 h1:ZoFioDKJxkSIW2otF9T0aPtNlUwhdVCcuZh/rzH9Hus= github.com/aws/smithy-go v1.27.0/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= +github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= +github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/buger/jsonparser v1.2.0 h1:4EFcvK1kD4jyj6YqNK6skK6w+y7FHHBR+XBCtxwu/6g= +github.com/buger/jsonparser v1.2.0/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= @@ -83,6 +87,8 @@ github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= +github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= +github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -100,6 +106,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= +github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -160,6 +168,8 @@ github.com/hashicorp/vault/api/auth/kubernetes v0.12.0 h1:DTrUMNXjpWEFMcU0FY1Eza github.com/hashicorp/vault/api/auth/kubernetes v0.12.0/go.mod h1:njyxrmFPtMuEPpPMZeemwhHovzC22hq2OuJtScI3iFc= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E= +github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= @@ -197,6 +207,8 @@ github.com/lucasepe/codename v0.2.0 h1:zkW9mKWSO8jjVIYFyZWE9FPvBtFVJxgMpQcMkf4Vv github.com/lucasepe/codename v0.2.0/go.mod h1:RDcExRuZPWp5Uz+BosvpROFTrxpt5r1vSzBObHdBdDM= github.com/magefile/mage v1.17.2 h1:fyXVu1eadI8Ap1HCCNgEhJ5McIWiYhLR8uol64ZZc40= github.com/magefile/mage v1.17.2/go.mod h1:Yj51kqllmsgFpvvSzgrZPK9WtluG3kUhFaBUVLo4feA= +github.com/mailru/easyjson v0.9.2 h1:dX8U45hQsZpxd80nLvDGihsQ/OxlvTkVUXH2r/8cb2M= +github.com/mailru/easyjson v0.9.2/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -257,8 +269,13 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= +github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= +github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= @@ -279,6 +296,8 @@ github.com/tinylib/msgp v1.5.0 h1:GWnqAE54wmnlFazjq2+vgr736Akg58iiHImh+kPY2pc= github.com/tinylib/msgp v1.5.0/go.mod h1:cvjFkb4RiC8qSBOPMGPSzSAx47nAsfhLVTCZZNuHv5o= github.com/ttab/elephant-api v0.24.1-0.20260617083910-2e37458d4b02 h1:HmKodM9ORkVoULvCSXuacdaPVlWITuzHKvoeGvf4UO4= github.com/ttab/elephant-api v0.24.1-0.20260617083910-2e37458d4b02/go.mod h1:4yLd0PwZRjsRj0Lb7QSQPaoYvz0zYZ7fALe1ANaYOhg= +github.com/ttab/elephant-api v0.24.1-0.20260617093243-b6e87ecd0563 h1:9Q4T1Jrsnfiw60voG8NaIbjSmCq4FNI4OL140UkZRyo= +github.com/ttab/elephant-api v0.24.1-0.20260617093243-b6e87ecd0563/go.mod h1:4yLd0PwZRjsRj0Lb7QSQPaoYvz0zYZ7fALe1ANaYOhg= github.com/ttab/elephantine v0.27.1 h1:kDX9KsPBT4r9DUN3iRqFxhnhA71t3nMTKJbmvCtat14= github.com/ttab/elephantine v0.27.1/go.mod h1:z6v4f7dEiAeOydoHAJ2HkJ6eAjTgvF8HBTOxE73NY1k= github.com/ttab/eltest v0.2.2 h1:T9+XZdLQJQciy7U/Q+TZskaxpnwdRJKEtaSz0699H+4= @@ -299,10 +318,15 @@ github.com/ttab/revisorschemas v1.5.0 h1:Zgu2Ib8vulpBIyqzQwKItoIH8KjFQcTILp60YM7 github.com/ttab/revisorschemas v1.5.0/go.mod h1:+bgejqvLIJSUCWmdg4oq0K3inHR7GN7NRwswjsUz694= github.com/twitchtv/twirp v8.1.3+incompatible h1:+F4TdErPgSUbMZMwp13Q/KgDVuI7HJXP61mNV3/7iuU= github.com/twitchtv/twirp v8.1.3+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A= +github.com/urfave/cli v1.22.16 h1:MH0k6uJxdwdeWQTwhSO42Pwr4YLrNLwBtg1MRgTqPdQ= +github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU= +github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4= github.com/urfave/cli/v3 v3.9.0 h1:AV9lIiPv3ukYnxunaCUsHnEozptYmDN2F0+yWqLMn/c= github.com/urfave/cli/v3 v3.9.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso= github.com/viccon/sturdyc v1.1.5 h1:GLQDnsyKt3L/tpdWCIARIRefn+5DAyvqu+0irBwt+vk= github.com/viccon/sturdyc v1.1.5/go.mod h1:OCBEgG/i48uugKQ498UQlfMHmf5j8MYY8a4BApfVnMo= +github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= +github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -310,6 +334,8 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAzt5X7s6266i6cSVkkFPS0TuXWbIg= +github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= diff --git a/index/search_api.go b/index/search_api.go index 55c4701..7aa802e 100644 --- a/index/search_api.go +++ b/index/search_api.go @@ -82,9 +82,11 @@ type SearchServiceV1 struct { subscriptions *sturdyc.Client[userSub] } -// GetFlatDocument implements index.SearchV1. It fetches a document directly -// from the repository and returns it in the flattened representation used for -// indexing, bypassing the eventual consistency of OpenSearch. +// GetFlatDocument implements index.SearchV1. By default it fetches a document +// directly from the repository and returns it in the flattened representation +// used for indexing, bypassing the eventual consistency of OpenSearch. If +// stored is set it instead returns the flattened document as it is currently +// stored in the active index. func (s *SearchServiceV1) GetFlatDocument( ctx context.Context, req *index.GetFlatDocumentRequest, ) (*index.GetFlatDocumentResponse, error) { @@ -97,6 +99,18 @@ func (s *SearchServiceV1) GetFlatDocument( return nil, twirp.RequiredArgumentError("uuid") } + if req.Stored { + return s.storedFlatDocument(ctx, auth, req) + } + + return s.convertFlatDocument(ctx, auth, req) +} + +// convertFlatDocument fetches the document from the repository and flattens it. +func (s *SearchServiceV1) convertFlatDocument( + ctx context.Context, + auth *elephantine.AuthInfo, req *index.GetFlatDocumentRequest, +) (*index.GetFlatDocumentResponse, error) { // Forward the authentication header so that the repository applies the // caller's read permissions. authCtx, err := twirp.WithHTTPRequestHeaders(ctx, http.Header{ @@ -130,22 +144,14 @@ func (s *SearchServiceV1) GetFlatDocument( return nil, twirp.InternalErrorf("build document state: %w", err) } - if req.DocumentType != "" { - state.Document.Type = req.DocumentType - } - - languageCode := req.Language - if languageCode == "" { - languageCode = state.Document.Language - } - // Use a per-request resolver as the cache it maintains isn't safe for // concurrent use. - language, err := NewLanguageResolver(s.languages).GetLanguageInfo(languageCode) + language, err := NewLanguageResolver(s.languages).GetLanguageInfo( + state.Document.Language) if err != nil { return nil, twirp.InvalidArgumentError("language", - fmt.Sprintf("could not resolve language %q: %v", - languageCode, err)) + fmt.Sprintf("could not resolve document language %q: %v", + state.Document.Language, err)) } flat, err := BuildDocument( @@ -155,7 +161,7 @@ func (s *SearchServiceV1) GetFlatDocument( } res := index.GetFlatDocumentResponse{ - Fields: make(map[string]*index.FieldValuesV1), + Fields: make(map[string]*index.FieldValuesV1, len(flat.Fields)), Document: docRes.Document, } @@ -168,6 +174,101 @@ func (s *SearchServiceV1) GetFlatDocument( return &res, nil } +// storedFlatDocument returns the flattened document as it is currently stored +// in the active index. +func (s *SearchServiceV1) storedFlatDocument( + ctx context.Context, + auth *elephantine.AuthInfo, req *index.GetFlatDocumentRequest, +) (_ *index.GetFlatDocumentResponse, outErr error) { + client, indexSet := s.active.GetActiveIndex() + if client == nil { + return nil, twirp.FailedPrecondition.Error("no active index") + } + + // Look the document up by ID across the active index set. Going through + // NewSearchRequest applies the caller's read restrictions. + query := &index.QueryRequestV1{ + Query: &index.QueryV1{ + Conditions: &index.QueryV1_Term{ + Term: &index.TermQueryV1{Field: "_id", Value: req.Uuid}, + }, + }, + Source: true, + Size: 1, + } + + osReq, err := internal.NewSearchRequest(auth, query) + if err != nil { + return nil, twirp.InternalErrorf("create search request: %w", err) + } + + queryPayload, err := json.Marshal(osReq) + if err != nil { + return nil, twirp.InternalErrorf("marshal opensearch query: %w", err) + } + + res, err := client.Search( + client.Search.WithContext(ctx), + client.Search.WithIndex(internal.IndexPattern(indexSet, query)), + client.Search.WithBody(bytes.NewReader(queryPayload))) + if err != nil { + return nil, twirp.InternalErrorf( + "perform opensearch search request: %w", err) + } + + defer func() { + err := res.Body.Close() + if err != nil { + outErr = errors.Join(outErr, fmt.Errorf( + "close opensearch response body: %w", err)) + } + }() + + dec := json.NewDecoder(res.Body) + + if res.IsError() { + var elasticErr ElasticErrorResponse + + err := dec.Decode(&elasticErr) + if err != nil { + return nil, errors.Join( + fmt.Errorf("opensearch responded with: %s", res.Status()), + fmt.Errorf("decode error response: %w", err), + ) + } + + return nil, twirp.InternalErrorf( + "error response from opensearch: %s", res.Status()) + } + + var response searchResponse + + err = dec.Decode(&response) + if err != nil { + return nil, twirp.InternalErrorf( + "unmarshal opensearch response: %w", err) + } + + if len(response.Hits.Hits) == 0 { + return nil, twirp.NotFoundError( + "no stored document with that UUID in the active index") + } + + hit := response.Hits.Hits[0] + + out := index.GetFlatDocumentResponse{ + Fields: make(map[string]*index.FieldValuesV1, len(hit.Source)), + } + + for field, values := range hit.Source { + out.Fields[field] = &index.FieldValuesV1{ + Values: anySliceToStrings(values), + } + } + + return &out, nil +} + // EndSubscription implements index.SearchV1. func (s *SearchServiceV1) EndSubscription( _ context.Context, _ *index.EndSubscriptionRequest, diff --git a/index/search_api_test.go b/index/search_api_test.go index b79b398..df52ec0 100644 --- a/index/search_api_test.go +++ b/index/search_api_test.go @@ -4,6 +4,7 @@ import ( "log/slog" "path/filepath" "testing" + "time" "github.com/golang-jwt/jwt/v5" "github.com/ttab/elephant-api/index" @@ -32,28 +33,62 @@ func TestGetFlatDocument(t *testing.T) { const russiaUUID = "f5d2e4c5-01ba-4dae-9f09-a86701e06ecd" - // The flattened document is fetched directly from the repository, so it's + // The converted document is fetched directly from the repository, so it's // available without waiting for OpenSearch to catch up. - res, err := search.GetFlatDocument(ctx, &index.GetFlatDocumentRequest{ + live, err := search.GetFlatDocument(ctx, &index.GetFlatDocumentRequest{ Uuid: russiaUUID, }) - test.Must(t, err, "get flattened document") + test.Must(t, err, "get converted flattened document") - test.Equal(t, russiaUUID, res.Document.Uuid, "returned document UUID") + test.Equal(t, russiaUUID, live.Document.Uuid, "returned document UUID") test.EqualDiff(t, []string{"Rysslands ambassadör kallas upp"}, - flatFieldValues(res, "document.title"), + flatFieldValues(live, "document.title"), "flattened document title") test.EqualDiff(t, []string{"core://newscoverage/" + russiaUUID}, - flatFieldValues(res, "document.uri"), + flatFieldValues(live, "document.uri"), "flattened document URI") test.EqualDiff(t, []string{"1"}, - flatFieldValues(res, "current_version"), + flatFieldValues(live, "current_version"), "flattened current version") + + // The stored document becomes available once indexing has caught up. It + // should be identical to the document converted directly from the repo. + var stored *index.GetFlatDocumentResponse + + deadline := time.After(10 * time.Second) + + for stored == nil { + select { + case <-ctx.Done(): + t.Fatal("cancelled while waiting for the document to be indexed") + case <-deadline: + t.Fatalf("timed out waiting for the document to be indexed,"+ + " last error: %v", err) + case <-time.After(200 * time.Millisecond): + } + + var res *index.GetFlatDocumentResponse + + res, err = search.GetFlatDocument(ctx, &index.GetFlatDocumentRequest{ + Uuid: russiaUUID, + Stored: true, + }) + if err == nil { + stored = res + } + } + + if stored.Document != nil { + t.Error("the stored response should not include the source document") + } + + test.EqualDiff(t, allFlatFields(live), allFlatFields(stored), + "stored fields match the converted fields") } func TestGetFlatDocumentRequiresUUID(t *testing.T) { @@ -80,6 +115,16 @@ func flatFieldValues( return f.Values } +func allFlatFields(res *index.GetFlatDocumentResponse) map[string][]string { + out := make(map[string][]string, len(res.Fields)) + + for name, values := range res.Fields { + out[name] = values.Values + } + + return out +} + func TestIndexPattern(t *testing.T) { test.Equal(t, "documents-foo-*-*", internal.IndexPattern("foo", &index.QueryRequestV1{}), From c880cc38ddc0c2230d91c3e9bb09baca65f4b93b Mon Sep 17 00:00:00 2001 From: Hugo Wetterberg Date: Wed, 17 Jun 2026 13:15:16 +0200 Subject: [PATCH 3/6] fix golangci-lint v2.12 findings and migrate config Bump the pinned golangci-lint to v2.12.2 and migrate gomodguard to gomodguard_v2. Quiet the newly-strict goconst (ignore tests and a set of generic serialization/label keys that mean different things in different contexts) and disable the informational govet inline analyzer. Extract header name constants now that there's more than one header literal. --- .github/workflows/lint.yaml | 2 +- .golangci.yml | 13 ++++++++++++- index/permissions.go | 7 +++++++ index/proxy.go | 2 +- index/search.go | 2 +- index/search_api.go | 4 ++-- index/server.go | 2 +- 7 files changed, 25 insertions(+), 7 deletions(-) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 0ffb95d..fee693e 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -18,5 +18,5 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v9 with: - version: v2.11.4 + version: v2.12.2 args: --timeout=4m diff --git a/.golangci.yml b/.golangci.yml index d28fb2f..4957cdd 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -15,7 +15,7 @@ linters: - godox - goheader - gomoddirectives - - gomodguard + - gomodguard_v2 - goprintffuncname - gosec - importas @@ -40,6 +40,17 @@ linters: - wrapcheck - wsl_v5 settings: + goconst: + # Test fixtures legitimately repeat string literals. + ignore-tests: true + # Common serialization keys and metric label names that mean different + # things in different contexts, so a shared constant would be misleading. + ignore-string-values: ^(type|name|index|query|rel)$ + govet: + # The inline analyzer only emits informational "cannot inline" notices, + # not actual problems. + disable: + - inline godox: keywords: - FIXME diff --git a/index/permissions.go b/index/permissions.go index 28272a5..90f376d 100644 --- a/index/permissions.go +++ b/index/permissions.go @@ -13,6 +13,13 @@ const ( ScopeSearch = "search" ) +// HTTP header names. +const ( + // headerAuthorization carries the bearer token. + headerAuthorization = "Authorization" + headerContentType = "Content-Type" +) + func RequireAnyScope(ctx context.Context, scopes ...string) (*elephantine.AuthInfo, error) { auth, ok := elephantine.GetAuthInfo(ctx) if !ok { diff --git a/index/proxy.go b/index/proxy.go index b84aa22..6d41f59 100644 --- a/index/proxy.go +++ b/index/proxy.go @@ -61,7 +61,7 @@ func (ep *ElasticProxy) searchHandler( ) (outErr error) { ctx := r.Context() - authorization := r.Header.Get("Authorization") + authorization := r.Header.Get(headerAuthorization) if authorization == "" { return ElasticErrorf( ErrorTypeUnauthorized, diff --git a/index/search.go b/index/search.go index f5522f9..bc3be4b 100644 --- a/index/search.go +++ b/index/search.go @@ -189,7 +189,7 @@ func ElasticHandler( ee = e } - w.Header().Set("Content-Type", "application/json") + w.Header().Set(headerContentType, "application/json") w.WriteHeader(ee.Status) enc := json.NewEncoder(w) diff --git a/index/search_api.go b/index/search_api.go index 7aa802e..ecd7896 100644 --- a/index/search_api.go +++ b/index/search_api.go @@ -114,7 +114,7 @@ func (s *SearchServiceV1) convertFlatDocument( // Forward the authentication header so that the repository applies the // caller's read permissions. authCtx, err := twirp.WithHTTPRequestHeaders(ctx, http.Header{ - "Authorization": []string{"Bearer " + auth.Token}, + headerAuthorization: []string{"Bearer " + auth.Token}, }) if err != nil { return nil, twirp.InternalErrorf("invalid header handling: %w", err) @@ -1008,7 +1008,7 @@ func (s *SearchServiceV1) processSearchResponse( // Forward the authentication header. authCtx, err := twirp.WithHTTPRequestHeaders(ctx, http.Header{ - "Authorization": []string{"Bearer " + auth.Token}, + headerAuthorization: []string{"Bearer " + auth.Token}, }) if err != nil { return nil, twirp.InternalErrorf( diff --git a/index/server.go b/index/server.go index 38dcf86..1669c06 100644 --- a/index/server.go +++ b/index/server.go @@ -110,7 +110,7 @@ func RunIndex(ctx context.Context, p Parameters) error { AllowInsecureLocalhost: true, Hosts: []string{"localhost", "tt.se"}, AllowedMethods: []string{"GET"}, - AllowedHeaders: []string{"Authorization", "Content-Type"}, + AllowedHeaders: []string{headerAuthorization, headerContentType}, }, proxy) server.Mux.Handle("/", proxyHandler) From e05898a4a29230c58e1f98b216eed63bf5a230f2 Mon Sep 17 00:00:00 2001 From: Hugo Wetterberg Date: Wed, 17 Jun 2026 13:48:01 +0200 Subject: [PATCH 4/6] replace golang.org/x/exp slices and maps with stdlib The exp packages carry //go:fix inline directives flagged by the govet inline analyzer; the used functions (slices.Contains, maps.Equal) have been in the standard library since Go 1.21. Migrating drops the golang.org/x/exp dependency and lets us re-enable the inline analyzer. --- .golangci.yml | 5 ----- go.mod | 12 ------------ go.sum | 28 ---------------------------- index/build.go | 2 +- index/document.go | 2 +- index/schemas.go | 2 +- 6 files changed, 3 insertions(+), 48 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 4957cdd..a7b20cf 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -46,11 +46,6 @@ linters: # Common serialization keys and metric label names that mean different # things in different contexts, so a shared constant would be misleading. ignore-string-values: ^(type|name|index|query|rel)$ - govet: - # The inline analyzer only emits informational "cannot inline" notices, - # not actual problems. - disable: - - inline godox: keywords: - FIXME diff --git a/go.mod b/go.mod index 45faef0..745da30 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,6 @@ require ( github.com/twitchtv/twirp v8.1.3+incompatible github.com/urfave/cli/v3 v3.9.0 github.com/viccon/sturdyc v1.1.5 - golang.org/x/exp v0.0.0-20260603202125-055de637280b golang.org/x/oauth2 v0.36.0 golang.org/x/sync v0.20.0 google.golang.org/protobuf v1.36.11 @@ -64,21 +63,17 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.43.1 // indirect github.com/aws/smithy-go v1.27.0 // indirect github.com/aymerick/douceur v0.2.0 // indirect - github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/buger/jsonparser v1.2.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/containerd/continuity v0.4.5 // indirect github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/cli v29.0.0+incompatible // indirect github.com/docker/go-connections v0.6.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/fatih/structtag v1.2.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/go-jose/go-jose/v4 v4.1.4 // indirect @@ -99,7 +94,6 @@ require ( github.com/hashicorp/vault/api v1.23.0 // indirect github.com/hashicorp/vault/api/auth/kubernetes v0.12.0 // indirect github.com/huandu/xstrings v1.5.0 // indirect - github.com/invopop/jsonschema v0.13.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect @@ -109,7 +103,6 @@ require ( github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/klauspost/crc32 v1.3.0 // indirect github.com/magefile/mage v1.17.2 // indirect - github.com/mailru/easyjson v0.9.2 // indirect github.com/minio/crc64nvme v1.1.1 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/minio/minio-go/v7 v7.0.97 // indirect @@ -131,19 +124,14 @@ require ( github.com/prometheus/common v0.68.0 // indirect github.com/prometheus/procfs v0.20.1 // indirect github.com/rs/xid v1.6.0 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect - github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/tinylib/msgp v1.5.0 // indirect - github.com/urfave/cli/v2 v2.27.7 // indirect - github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect - github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect go.opentelemetry.io/otel v1.38.0 // indirect diff --git a/go.sum b/go.sum index 3b6de11..b0d4297 100644 --- a/go.sum +++ b/go.sum @@ -71,12 +71,8 @@ github.com/aws/smithy-go v1.27.0 h1:ZoFioDKJxkSIW2otF9T0aPtNlUwhdVCcuZh/rzH9Hus= github.com/aws/smithy-go v1.27.0/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= -github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= -github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/buger/jsonparser v1.2.0 h1:4EFcvK1kD4jyj6YqNK6skK6w+y7FHHBR+XBCtxwu/6g= -github.com/buger/jsonparser v1.2.0/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= @@ -87,8 +83,6 @@ github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= -github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= -github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -106,8 +100,6 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= -github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= -github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -168,8 +160,6 @@ github.com/hashicorp/vault/api/auth/kubernetes v0.12.0 h1:DTrUMNXjpWEFMcU0FY1Eza github.com/hashicorp/vault/api/auth/kubernetes v0.12.0/go.mod h1:njyxrmFPtMuEPpPMZeemwhHovzC22hq2OuJtScI3iFc= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E= -github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= @@ -207,8 +197,6 @@ github.com/lucasepe/codename v0.2.0 h1:zkW9mKWSO8jjVIYFyZWE9FPvBtFVJxgMpQcMkf4Vv github.com/lucasepe/codename v0.2.0/go.mod h1:RDcExRuZPWp5Uz+BosvpROFTrxpt5r1vSzBObHdBdDM= github.com/magefile/mage v1.17.2 h1:fyXVu1eadI8Ap1HCCNgEhJ5McIWiYhLR8uol64ZZc40= github.com/magefile/mage v1.17.2/go.mod h1:Yj51kqllmsgFpvvSzgrZPK9WtluG3kUhFaBUVLo4feA= -github.com/mailru/easyjson v0.9.2 h1:dX8U45hQsZpxd80nLvDGihsQ/OxlvTkVUXH2r/8cb2M= -github.com/mailru/easyjson v0.9.2/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -269,13 +257,8 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= -github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= -github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= @@ -294,8 +277,6 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tinylib/msgp v1.5.0 h1:GWnqAE54wmnlFazjq2+vgr736Akg58iiHImh+kPY2pc= github.com/tinylib/msgp v1.5.0/go.mod h1:cvjFkb4RiC8qSBOPMGPSzSAx47nAsfhLVTCZZNuHv5o= -github.com/ttab/elephant-api v0.24.1-0.20260617083910-2e37458d4b02 h1:HmKodM9ORkVoULvCSXuacdaPVlWITuzHKvoeGvf4UO4= -github.com/ttab/elephant-api v0.24.1-0.20260617083910-2e37458d4b02/go.mod h1:4yLd0PwZRjsRj0Lb7QSQPaoYvz0zYZ7fALe1ANaYOhg= github.com/ttab/elephant-api v0.24.1-0.20260617093243-b6e87ecd0563 h1:9Q4T1Jrsnfiw60voG8NaIbjSmCq4FNI4OL140UkZRyo= github.com/ttab/elephant-api v0.24.1-0.20260617093243-b6e87ecd0563/go.mod h1:4yLd0PwZRjsRj0Lb7QSQPaoYvz0zYZ7fALe1ANaYOhg= github.com/ttab/elephantine v0.27.1 h1:kDX9KsPBT4r9DUN3iRqFxhnhA71t3nMTKJbmvCtat14= @@ -318,15 +299,10 @@ github.com/ttab/revisorschemas v1.5.0 h1:Zgu2Ib8vulpBIyqzQwKItoIH8KjFQcTILp60YM7 github.com/ttab/revisorschemas v1.5.0/go.mod h1:+bgejqvLIJSUCWmdg4oq0K3inHR7GN7NRwswjsUz694= github.com/twitchtv/twirp v8.1.3+incompatible h1:+F4TdErPgSUbMZMwp13Q/KgDVuI7HJXP61mNV3/7iuU= github.com/twitchtv/twirp v8.1.3+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A= -github.com/urfave/cli v1.22.16 h1:MH0k6uJxdwdeWQTwhSO42Pwr4YLrNLwBtg1MRgTqPdQ= -github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU= -github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4= github.com/urfave/cli/v3 v3.9.0 h1:AV9lIiPv3ukYnxunaCUsHnEozptYmDN2F0+yWqLMn/c= github.com/urfave/cli/v3 v3.9.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso= github.com/viccon/sturdyc v1.1.5 h1:GLQDnsyKt3L/tpdWCIARIRefn+5DAyvqu+0irBwt+vk= github.com/viccon/sturdyc v1.1.5/go.mod h1:OCBEgG/i48uugKQ498UQlfMHmf5j8MYY8a4BApfVnMo= -github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= -github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -334,8 +310,6 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAzt5X7s6266i6cSVkkFPS0TuXWbIg= -github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= @@ -359,8 +333,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= -golang.org/x/exp v0.0.0-20260603202125-055de637280b h1:v1uXiEBHo8QA0LiGCo7UgHMzHT4Kdfpl2zmtH5vaP1Q= -golang.org/x/exp v0.0.0-20260603202125-055de637280b/go.mod h1:d2fgXJLVs4dYDHUk5lwMIfzRzSrWCfGZb0ZqeLa/Vcw= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= diff --git a/index/build.go b/index/build.go index e5d353e..7605b71 100644 --- a/index/build.go +++ b/index/build.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "html" + "slices" "strings" "time" @@ -11,7 +12,6 @@ import ( "github.com/ttab/elephant-index/internal" "github.com/ttab/newsdoc" "github.com/ttab/revisor" - "golang.org/x/exp/slices" ) // Fields can depend on index settings (like custom normalisers). These won't be diff --git a/index/document.go b/index/document.go index aa1d0a5..f43df23 100644 --- a/index/document.go +++ b/index/document.go @@ -1,11 +1,11 @@ package index import ( + "slices" "strconv" "time" "github.com/ttab/newsdoc" - "golang.org/x/exp/slices" ) // DocumentState is the full state that we want to index. diff --git a/index/schemas.go b/index/schemas.go index c777a64..56e7688 100644 --- a/index/schemas.go +++ b/index/schemas.go @@ -5,13 +5,13 @@ import ( "encoding/json" "fmt" "log/slog" + "maps" "sync" "time" "github.com/ttab/elephant-api/repository" "github.com/ttab/elephantine" "github.com/ttab/revisor" - "golang.org/x/exp/maps" ) type SchemaLoader struct { From 3af4ca0cbd4643d8fbdbc536954f01c7e7a4de7e Mon Sep 17 00:00:00 2001 From: Hugo Wetterberg Date: Wed, 17 Jun 2026 14:00:40 +0200 Subject: [PATCH 5/6] update to elephant-api v0.24.1 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 745da30..50cb34f 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/opensearch-project/opensearch-go/v2 v2.3.0 github.com/ory/dockertest/v3 v3.12.0 github.com/prometheus/client_golang v1.23.2 - github.com/ttab/elephant-api v0.24.1-0.20260617093243-b6e87ecd0563 + github.com/ttab/elephant-api v0.24.1 github.com/ttab/elephantine v0.27.1 github.com/ttab/eltest v0.2.2 github.com/ttab/flerr v0.1.0 diff --git a/go.sum b/go.sum index b0d4297..764ddb0 100644 --- a/go.sum +++ b/go.sum @@ -277,8 +277,8 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tinylib/msgp v1.5.0 h1:GWnqAE54wmnlFazjq2+vgr736Akg58iiHImh+kPY2pc= github.com/tinylib/msgp v1.5.0/go.mod h1:cvjFkb4RiC8qSBOPMGPSzSAx47nAsfhLVTCZZNuHv5o= -github.com/ttab/elephant-api v0.24.1-0.20260617093243-b6e87ecd0563 h1:9Q4T1Jrsnfiw60voG8NaIbjSmCq4FNI4OL140UkZRyo= -github.com/ttab/elephant-api v0.24.1-0.20260617093243-b6e87ecd0563/go.mod h1:4yLd0PwZRjsRj0Lb7QSQPaoYvz0zYZ7fALe1ANaYOhg= +github.com/ttab/elephant-api v0.24.1 h1:nxBM8ZRbAjcqnxhplqP9GuWZ5EwTsK3t4eozLj9niwU= +github.com/ttab/elephant-api v0.24.1/go.mod h1:4yLd0PwZRjsRj0Lb7QSQPaoYvz0zYZ7fALe1ANaYOhg= github.com/ttab/elephantine v0.27.1 h1:kDX9KsPBT4r9DUN3iRqFxhnhA71t3nMTKJbmvCtat14= github.com/ttab/elephantine v0.27.1/go.mod h1:z6v4f7dEiAeOydoHAJ2HkJ6eAjTgvF8HBTOxE73NY1k= github.com/ttab/eltest v0.2.2 h1:T9+XZdLQJQciy7U/Q+TZskaxpnwdRJKEtaSz0699H+4= From b96b564e44faf521bfeadb28f2cf8a2a1dd26f1f Mon Sep 17 00:00:00 2001 From: Hugo Wetterberg Date: Wed, 17 Jun 2026 14:06:32 +0200 Subject: [PATCH 6/6] fix goconst ignore-string-values config format The golangci-lint action runs 'config verify', which requires ignore-string-values to be an array of patterns rather than a single string. 'golangci-lint run' accepts the scalar form, so this only surfaced in CI. --- .golangci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.golangci.yml b/.golangci.yml index a7b20cf..7b99ab0 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -45,7 +45,8 @@ linters: ignore-tests: true # Common serialization keys and metric label names that mean different # things in different contexts, so a shared constant would be misleading. - ignore-string-values: ^(type|name|index|query|rel)$ + ignore-string-values: + - ^(type|name|index|query|rel)$ godox: keywords: - FIXME