From bd7c4aee4aa8831e24e8820edda3ac677dee749a Mon Sep 17 00:00:00 2001 From: "Theo@tsa.team" Date: Mon, 23 Jun 2025 11:34:15 +0300 Subject: [PATCH 001/126] feat: :sparkles: Add typesense support to compose file, go server and adjust needed dependencies [RECORD-SEARCH] --- .gitignore | 3 + backend/pkg/database/gorm_common.go | 39 ++++++++++- backend/pkg/search/typesense.go | 77 +++++++++++++++++++++ backend/pkg/search/typesense_client.go | 81 ++++++++++++++++++++++ backend/pkg/web/handler/search.go | 33 +++++++++ backend/pkg/web/server.go | 8 +++ docker-compose.yml | 18 ++++- go.mod | 41 +++++++----- go.sum | 93 ++++++++++++++++---------- 9 files changed, 336 insertions(+), 57 deletions(-) create mode 100644 backend/pkg/search/typesense.go create mode 100644 backend/pkg/search/typesense_client.go create mode 100644 backend/pkg/web/handler/search.go diff --git a/.gitignore b/.gitignore index e44a5a4a8..814545fdf 100644 --- a/.gitignore +++ b/.gitignore @@ -81,3 +81,6 @@ fasten.db-wal backend/resources/related_versions.json frontend/documentation.json + +# Typesense server data +typesense-data/ diff --git a/backend/pkg/database/gorm_common.go b/backend/pkg/database/gorm_common.go index d876e739d..306c85715 100644 --- a/backend/pkg/database/gorm_common.go +++ b/backend/pkg/database/gorm_common.go @@ -15,6 +15,7 @@ import ( "github.com/fastenhealth/fasten-onprem/backend/pkg/event_bus" "github.com/fastenhealth/fasten-onprem/backend/pkg/models" databaseModel "github.com/fastenhealth/fasten-onprem/backend/pkg/models/database" + typesenseClient "github.com/fastenhealth/fasten-onprem/backend/pkg/search" "github.com/fastenhealth/fasten-onprem/backend/pkg/utils" sourceModel "github.com/fastenhealth/fasten-sources/clients/models" "github.com/gin-gonic/gin" @@ -367,8 +368,44 @@ func (gr *GormRepository) UpsertRawResource(ctx context.Context, sourceCredentia } } - return gr.UpsertResource(ctx, wrappedResourceModel) + success, err := gr.UpsertResource(ctx, wrappedResourceModel) + if err != nil { + return false, err + } + + // Add nil checks before launching goroutine + if wrappedResourceModel == nil { + gr.Logger.Warn("Cannot index nil resource in Typesense") + return success, nil + } + + if typesenseClient.Client == nil { + gr.Logger.Warn("Typesense client is nil, skipping indexing") + return success, nil + } + + searchClient := &typesenseClient.SearchClient{Client: typesenseClient.Client} + + // Launch goroutine with proper error handling + go func(resource *models.ResourceBase) { + // Additional safety check inside goroutine + if resource == nil { + gr.Logger.Warn("Resource is nil in indexing goroutine") + return + } + + if searchClient == nil { + gr.Logger.Warn("SearchClient is nil in indexing goroutine") + return + } + + err := searchClient.IndexResource(resource) + if err != nil { + gr.Logger.Warnf("Failed to index resource in Typesense: %v", err) + } + }(wrappedResourceModel) + return success, nil } func (gr *GormRepository) UpsertRawResourceAssociation( diff --git a/backend/pkg/search/typesense.go b/backend/pkg/search/typesense.go new file mode 100644 index 000000000..fe63d32e7 --- /dev/null +++ b/backend/pkg/search/typesense.go @@ -0,0 +1,77 @@ +package search + +import ( + "context" + "errors" + "log" + "os" + "time" + + "github.com/sirupsen/logrus" + "github.com/typesense/typesense-go/typesense" + "github.com/typesense/typesense-go/typesense/api" +) + +var Client *typesense.Client + +func waitForTypesense(client *typesense.Client, maxRetries int, delay time.Duration) error { + for i := 0; i < maxRetries; i++ { + _, err := client.Collections().Retrieve(context.Background()) // simple ping to check if Typesense is ready + if err == nil { + return nil // Typesense is ready + } + log.Printf("Typesense not ready yet (attempt %d/%d): %v", i+1, maxRetries, err) + time.Sleep(delay) + } + return errors.New("typesense did not become ready in time") +} + +func Init(logger *logrus.Entry) error { + apiKey := os.Getenv("TYPESENSE_API_KEY") + if apiKey == "" { + log.Fatal("TYPESENSE_API_KEY not set") + } + + Client = typesense.NewClient( + typesense.WithServer("http://typesense:8108"), + typesense.WithAPIKey(apiKey)) + + log.Printf("Typesense client initialized with server: %s", "http://typesense:8108") + + err := waitForTypesense(Client, 10, 2*time.Second) + if err != nil { + log.Fatalf("ERROR: failed to initialize Typesense: %v", err) + } + + // Check if "resources" collection exists + _, err = Client.Collection("resources").Retrieve(context.Background()) + if err == nil { + logger.Info("📦 Typesense collection 'resources' already exists") + } else { + logger.Info("📦 Creating Typesense collection 'resources'...") + + optionalTrue := true + _, err = Client.Collections().Create(context.Background(), &api.CollectionSchema{ + Name: "resources", + Fields: []api.Field{ + {Name: "id", Type: "string"}, + {Name: "user_id", Type: "string"}, + {Name: "source_id", Type: "string"}, + {Name: "source_resource_type", Type: "string"}, + {Name: "source_resource_id", Type: "string"}, + {Name: "sort_date", Type: "float"}, + {Name: "sort_title", Type: "string", Optional: &optionalTrue}, + {Name: "source_uri", Type: "string", Optional: &optionalTrue}, + }, + DefaultSortingField: func(s string) *string { return &s }("sort_date"), + }) + if err != nil { + logger.WithError(err).Error("❌ Failed to create 'resources' collection in Typesense") + return err + } + logger.Info("✅ Created 'resources' collection") + } + + logger.Info("✅ Typesense client initialized and collection ready") + return nil +} diff --git a/backend/pkg/search/typesense_client.go b/backend/pkg/search/typesense_client.go new file mode 100644 index 000000000..a76d7182a --- /dev/null +++ b/backend/pkg/search/typesense_client.go @@ -0,0 +1,81 @@ +package search + +import ( + "context" + "fmt" + "time" + + "github.com/fastenhealth/fasten-onprem/backend/pkg/models" + "github.com/google/uuid" + "github.com/typesense/typesense-go/typesense" + "github.com/typesense/typesense-go/typesense/api" +) + +type SearchClient struct { + Client *typesense.Client +} + +func (s *SearchClient) IndexResource(resource *models.ResourceBase) error { + + print("Indexing resource in Typesense: ", resource) + doc := map[string]interface{}{ + "id": uuid.New().String(), // Generate a new UUID for the document ID in order to avoid overlaps + "sort_title": derefStr(resource.SortTitle), + "source_uri": derefStr(resource.SourceUri), + "user_id": resource.UserID.String(), + "source_id": resource.SourceID.String(), + "source_resource_type": resource.SourceResourceType, + "source_resource_id": resource.SourceResourceID, + "sort_date": float64(time.Now().Unix()), + "resource_raw": resource.ResourceRaw, + } + + if resource.SortDate != nil { + doc["sort_date"] = resource.SortDate.Unix() + } + + if resource.SortTitle != nil { + doc["sort_title"] = *resource.SortTitle + } + + if resource.SourceUri != nil { + doc["source_uri"] = *resource.SourceUri + } + + _, err := s.Client.Collection("resources").Documents().Upsert(context.Background(), doc) + if err != nil { + return fmt.Errorf("failed to index resource: %w", err) + } + + return nil +} + +func (s *SearchClient) SearchResources(query string) ([]map[string]interface{}, error) { + searchParams := &api.SearchCollectionParams{ + Q: query, + QueryBy: "sort_title,source_resource_type,source_resource_id,source_uri", + // Add other search params as needed (e.g., filters, pagination) + } + + resp, err := s.Client.Collection("resources").Documents().Search(context.Background(), searchParams) + if err != nil { + return nil, err + } + + // resp.Hits contains the documents matched + results := make([]map[string]interface{}, 0, len(*resp.Hits)) + for _, hit := range *resp.Hits { + if hit.Document != nil { + results = append(results, *hit.Document) + } + } + + return results, nil +} + +func derefStr(s *string) string { + if s != nil { + return *s + } + return "" +} diff --git a/backend/pkg/web/handler/search.go b/backend/pkg/web/handler/search.go new file mode 100644 index 000000000..b5240b643 --- /dev/null +++ b/backend/pkg/web/handler/search.go @@ -0,0 +1,33 @@ +package handler + +import ( + "net/http" + + "github.com/fastenhealth/fasten-onprem/backend/pkg/database" + typesenseClient "github.com/fastenhealth/fasten-onprem/backend/pkg/search" + "github.com/gin-gonic/gin" +) + +type GormRepository struct { + *database.GormRepository +} + +func (gr *GormRepository) SearchResourcesHandler(c *gin.Context) { + query := c.Query("q") + if query == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "query parameter 'q' is required"}) + return + } + + searchClient := &typesenseClient.SearchClient{Client: typesenseClient.Client} + + results, err := searchClient.SearchResources(query) + if err != nil { + gr.Logger.Errorf("Typesense search error: %v", err) + c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"}) + return + } + + // Return JSON results + c.JSON(http.StatusOK, results) +} diff --git a/backend/pkg/web/server.go b/backend/pkg/web/server.go index fd5d2cd42..c2b970f48 100644 --- a/backend/pkg/web/server.go +++ b/backend/pkg/web/server.go @@ -16,6 +16,7 @@ import ( "github.com/fastenhealth/fasten-onprem/backend/pkg/database" "github.com/fastenhealth/fasten-onprem/backend/pkg/event_bus" "github.com/fastenhealth/fasten-onprem/backend/pkg/models" + "github.com/fastenhealth/fasten-onprem/backend/pkg/search" "github.com/fastenhealth/fasten-onprem/backend/pkg/web/handler" "github.com/fastenhealth/fasten-onprem/backend/pkg/web/middleware" "github.com/gin-gonic/gin" @@ -49,6 +50,7 @@ func (ae *AppEngine) Setup() (*gin.RouterGroup, *gin.Engine) { basePath := ae.Config.GetString("web.listen.basepath") ae.Logger.Debugf("basepath: %s", basePath) + searchHandler := &handler.GormRepository{} base := r.Group(basePath) { @@ -118,6 +120,7 @@ func (ae *AppEngine) Setup() (*gin.RouterGroup, *gin.Engine) { secure.POST("/resource/composition", handler.CreateResourceComposition) secure.POST("/resource/related", handler.CreateRelatedResources) secure.DELETE("/encounter/:encounterId/related/:resourceType/:resourceId", handler.EncounterUnlinkResource) + secure.GET("/resource/search", searchHandler.SearchResourcesHandler) secure.GET("/dashboards", handler.GetDashboard) secure.POST("/dashboards", handler.AddDashboardLocation) @@ -301,5 +304,10 @@ func (ae *AppEngine) Start() error { } r := ae.SetupFrontendRouting(baseRouterGroup, ginRouter) + err = search.Init(ae.Logger) + if err != nil { + return fmt.Errorf("failed to initialize Typesense: %w", err) + } + return r.Run(fmt.Sprintf("%s:%s", ae.Config.GetString("web.listen.host"), ae.Config.GetString("web.listen.port"))) } diff --git a/docker-compose.yml b/docker-compose.yml index dde19bc7d..1bb46802a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,10 +8,24 @@ services: context: . dockerfile: Dockerfile args: - - FASTEN_ENV=prod -# - FASTEN_ENV=sandbox +# - FASTEN_ENV=prod + - FASTEN_ENV=sandbox ports: - "9090:8080" volumes: - ./db:/opt/fasten/db # - ./config.example.yaml:/opt/fasten/config/config.yaml + environment: + - TYPESENSE_API_KEY=xyz123 + typesense: + image: typesense/typesense:0.25.1 + container_name: typesense + ports: + - "8108:8108" + volumes: + - ./typesense-data:/data + environment: + - TYPESENSE_API_KEY=xyz123 + - TYPESENSE_DATA_DIR=/data + - TYPESENSE_ENABLE_CORS=true + restart: unless-stopped \ No newline at end of file diff --git a/go.mod b/go.mod index b247bc980..09763c510 100644 --- a/go.mod +++ b/go.mod @@ -17,40 +17,45 @@ require ( github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3 github.com/fastenhealth/fasten-sources v0.6.25 github.com/fastenhealth/gofhir-models v0.0.7 - github.com/gin-gonic/gin v1.9.0 + github.com/gin-gonic/gin v1.9.1 github.com/go-gormigrate/gormigrate/v2 v2.1.1 github.com/golang-jwt/jwt/v4 v4.4.2 github.com/golang/mock v1.6.0 github.com/google/go-github/v54 v54.0.0 - github.com/google/uuid v1.3.0 + github.com/google/uuid v1.5.0 github.com/iancoleman/strcase v0.2.0 github.com/lestrrat-go/jwx/v2 v2.0.11 github.com/samber/lo v1.35.0 github.com/sirupsen/logrus v1.9.0 github.com/spf13/viper v1.12.0 - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.9.0 + github.com/typesense/typesense-go v1.1.0 github.com/urfave/cli/v2 v2.11.2 - golang.org/x/crypto v0.14.0 + golang.org/x/crypto v0.23.0 golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 - golang.org/x/mod v0.15.0 - golang.org/x/net v0.17.0 + golang.org/x/mod v0.17.0 + golang.org/x/net v0.25.0 gorm.io/datatypes v1.0.7 gorm.io/driver/sqlite v1.5.4 gorm.io/gorm v1.25.4 ) require ( + github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect + github.com/chenzhuoyu/iasm v0.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgx/v5 v5.4.3 // indirect github.com/mattn/go-sqlite3 v1.14.17 // indirect + github.com/oapi-codegen/runtime v1.1.1 // indirect + github.com/sony/gobreaker v0.5.0 // indirect ) require ( github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect - github.com/bytedance/sonic v1.8.8 // indirect - github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/bytedance/sonic v1.10.0-rc3 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/cloudflare/circl v1.3.3 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -72,7 +77,7 @@ require ( github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/kvz/logstreamer v0.0.0-20150507115422-a635b98146f0 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/lestrrat-go/blackmagic v1.0.1 // indirect @@ -81,14 +86,14 @@ require ( github.com/lestrrat-go/iter v1.0.2 // indirect github.com/lestrrat-go/option v1.0.1 // indirect github.com/magiconair/properties v1.8.6 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.18 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.7 // indirect + github.com/pelletier/go-toml/v2 v2.0.9 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/seborama/govcr v4.5.0+incompatible // indirect @@ -99,16 +104,16 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.3.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.2.11 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect - golang.org/x/arch v0.3.0 // indirect + golang.org/x/arch v0.4.0 // indirect golang.org/x/oauth2 v0.11.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/term v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/term v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.31.0 // indirect - gopkg.in/ini.v1 v1.66.4 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gorm.io/driver/mysql v1.3.2 // indirect diff --git a/go.sum b/go.sum index 2c3cf622f..c3902e5bb 100644 --- a/go.sum +++ b/go.sum @@ -44,16 +44,24 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA= github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/analogj/go-util v0.0.0-20210417161720-39b497cca03b h1:Y/+MfmdKPPpVY7C6ggt/FpltFSitlpUtyJEdcQyFXQg= github.com/analogj/go-util v0.0.0-20210417161720-39b497cca03b/go.mod h1:bRSzJXgXnT5+Ihah7RSC7Cvp16UmoLn3wq6ROciS1Ow= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.8.8 h1:Kj4AYbZSeENfyXicsYppYKO0K2YWab+i2UTSY7Ukz9Q= -github.com/bytedance/sonic v1.8.8/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= +github.com/bytedance/sonic v1.10.0-rc3 h1:uNSnscRapXTwUgTyOF0GVljYD08p9X/Lbr9MweSV3V0= +github.com/bytedance/sonic v1.10.0-rc3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= +github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= +github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= @@ -115,8 +123,8 @@ github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uq github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= -github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -211,8 +219,8 @@ github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= @@ -270,6 +278,7 @@ github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jgiannuzzi/go-sqlite3 v1.14.17-0.20230719111531-6e53453ccbd3 h1:Iqr8ZWwijosAcawoD9IZ7cwj61WH50Rysm+RQ+ZIB4I= github.com/jgiannuzzi/go-sqlite3 v1.14.17-0.20230719111531-6e53453ccbd3/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/jinzhu/copier v0.3.4 h1:mfU6jI9PtCeUjkjQ322dlff9ELjGDu975C2p/nrubVI= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= @@ -279,10 +288,12 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= -github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= @@ -322,15 +333,16 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -343,10 +355,12 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= +github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= +github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us= -github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= +github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -375,6 +389,8 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= +github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= @@ -385,6 +401,7 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= +github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -400,15 +417,18 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI= github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= -github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/typesense/typesense-go v1.1.0 h1:QocehDarVXRArMIosPIdawiVFZZbnRkPJxwnAGOFkzw= +github.com/typesense/typesense-go v1.1.0/go.mod h1:KcPODU7ltrcUFC/gygMTkAAfZ9M8/q6ayrdl1MnE1kI= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/urfave/cli/v2 v2.11.2 h1:FVfNg4m3vbjbBpLYxW//WjxUoHvJ9TlppXcqY9Q9ZfA= github.com/urfave/cli/v2 v2.11.2/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= @@ -430,6 +450,7 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= @@ -438,8 +459,8 @@ go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= -golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc= +golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= @@ -457,8 +478,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -497,8 +518,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= -golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -537,8 +558,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -609,26 +630,25 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -640,8 +660,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -805,8 +825,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= -gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= -gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= @@ -838,6 +858,7 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= From a0fdaadeabb771256cd477004bfcb759d6c2424e Mon Sep 17 00:00:00 2001 From: "Theo@tsa.team" Date: Tue, 24 Jun 2025 15:45:28 +0300 Subject: [PATCH 002/126] feat: :sparkles: Fix search API call in server, add logs and type param support [RECORD-SEARCH] --- backend/pkg/search/typesense.go | 4 ++ backend/pkg/search/typesense_client.go | 34 ++++++++++++++-- backend/pkg/web/handler/search.go | 54 ++++++++++++++++++++++++-- backend/pkg/web/server.go | 3 +- 4 files changed, 86 insertions(+), 9 deletions(-) diff --git a/backend/pkg/search/typesense.go b/backend/pkg/search/typesense.go index fe63d32e7..e8a6397bb 100644 --- a/backend/pkg/search/typesense.go +++ b/backend/pkg/search/typesense.go @@ -51,6 +51,7 @@ func Init(logger *logrus.Entry) error { logger.Info("📦 Creating Typesense collection 'resources'...") optionalTrue := true + // Create the "resources" collection with the specified schema _, err = Client.Collections().Create(context.Background(), &api.CollectionSchema{ Name: "resources", Fields: []api.Field{ @@ -62,8 +63,11 @@ func Init(logger *logrus.Entry) error { {Name: "sort_date", Type: "float"}, {Name: "sort_title", Type: "string", Optional: &optionalTrue}, {Name: "source_uri", Type: "string", Optional: &optionalTrue}, + {Name: "code_text", Type: "string", Optional: &optionalTrue}, + {Name: "resource_raw", Type: "object", Optional: &optionalTrue}, // JSON blob }, DefaultSortingField: func(s string) *string { return &s }("sort_date"), + EnableNestedFields: &optionalTrue, }) if err != nil { logger.WithError(err).Error("❌ Failed to create 'resources' collection in Typesense") diff --git a/backend/pkg/search/typesense_client.go b/backend/pkg/search/typesense_client.go index a76d7182a..addc7320e 100644 --- a/backend/pkg/search/typesense_client.go +++ b/backend/pkg/search/typesense_client.go @@ -2,6 +2,7 @@ package search import ( "context" + "encoding/json" "fmt" "time" @@ -36,11 +37,19 @@ func (s *SearchClient) IndexResource(resource *models.ResourceBase) error { if resource.SortTitle != nil { doc["sort_title"] = *resource.SortTitle + doc["code_text"] = *resource.SortTitle // fallback } if resource.SourceUri != nil { doc["source_uri"] = *resource.SourceUri } + // Extract `code.text` if present + var rawMap map[string]interface{} + if err := json.Unmarshal(resource.ResourceRaw, &rawMap); err == nil { + if text, ok := extractCodeText(rawMap); ok { + doc["code_text"] = text + } + } _, err := s.Client.Collection("resources").Documents().Upsert(context.Background(), doc) if err != nil { @@ -50,11 +59,16 @@ func (s *SearchClient) IndexResource(resource *models.ResourceBase) error { return nil } -func (s *SearchClient) SearchResources(query string) ([]map[string]interface{}, error) { +func (s *SearchClient) SearchResources(query string, resourceTypeFilter *string) ([]map[string]interface{}, error) { searchParams := &api.SearchCollectionParams{ Q: query, - QueryBy: "sort_title,source_resource_type,source_resource_id,source_uri", - // Add other search params as needed (e.g., filters, pagination) + QueryBy: "sort_title,code_text,source_resource_type,source_resource_id,source_uri", + SortBy: ptr("sort_date:desc"), + } + + if resourceTypeFilter != nil { + filter := fmt.Sprintf("source_resource_type:=%s", *resourceTypeFilter) + searchParams.FilterBy = &filter } resp, err := s.Client.Collection("resources").Documents().Search(context.Background(), searchParams) @@ -79,3 +93,17 @@ func derefStr(s *string) string { } return "" } + +func extractCodeText(raw map[string]interface{}) (string, bool) { + if code, ok := raw["code"].(map[string]interface{}); ok { + if text, ok := code["text"].(string); ok { + return text, true + } + } + return "", false +} + +// ptr returns a pointer to the given string. +func ptr(s string) *string { + return &s +} diff --git a/backend/pkg/web/handler/search.go b/backend/pkg/web/handler/search.go index b5240b643..1a44c942c 100644 --- a/backend/pkg/web/handler/search.go +++ b/backend/pkg/web/handler/search.go @@ -1,33 +1,79 @@ package handler import ( + "fmt" "net/http" "github.com/fastenhealth/fasten-onprem/backend/pkg/database" typesenseClient "github.com/fastenhealth/fasten-onprem/backend/pkg/search" "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" ) type GormRepository struct { *database.GormRepository } -func (gr *GormRepository) SearchResourcesHandler(c *gin.Context) { +func SearchResourcesHandler(c *gin.Context) { + // Get logger from context or use a default one + logger := logrus.WithFields(logrus.Fields{ + "handler": "SearchResourcesHandler", + "path": c.Request.URL.Path, + "method": c.Request.Method, + "ip": c.ClientIP(), + }) + + logger.Info("Search request started") + query := c.Query("q") + resourceTypeFilter := c.Query("type") if query == "" { + logger.Warn("Missing required query parameter 'q'") c.JSON(http.StatusBadRequest, gin.H{"error": "query parameter 'q' is required"}) return } + logger.Debug("Checking Typesense client availability") + if typesenseClient.Client == nil { + logger.Error("Typesense client is not initialized") + c.JSON(http.StatusServiceUnavailable, gin.H{"error": "search service unavailable"}) + return + } + + logger.Debug("Creating search client") searchClient := &typesenseClient.SearchClient{Client: typesenseClient.Client} - results, err := searchClient.SearchResources(query) + if searchClient == nil { + logger.Error("Failed to create search client") + c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"}) + return + } + + logger.WithFields(logrus.Fields{ + "query": query, + "type": resourceTypeFilter, + }).Info("Executing search query") + + // Only pass the filter if it's not empty + var filterPtr *string + if resourceTypeFilter != "" { + filterPtr = &resourceTypeFilter + logger.WithField("filter", resourceTypeFilter).Debug("Using resource type filter") + } else { + logger.Debug("No resource type filter provided - searching all types") + } + + results, err := searchClient.SearchResources(query, filterPtr) if err != nil { - gr.Logger.Errorf("Typesense search error: %v", err) + logger.WithError(err).Error("Search query failed") c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"}) return } - // Return JSON results + logger.WithFields(logrus.Fields{ + "resultCount": len(results), + "resultType": fmt.Sprintf("%T", results), + }).Info("Search query completed successfully") + c.JSON(http.StatusOK, results) } diff --git a/backend/pkg/web/server.go b/backend/pkg/web/server.go index c2b970f48..f619d006c 100644 --- a/backend/pkg/web/server.go +++ b/backend/pkg/web/server.go @@ -50,7 +50,6 @@ func (ae *AppEngine) Setup() (*gin.RouterGroup, *gin.Engine) { basePath := ae.Config.GetString("web.listen.basepath") ae.Logger.Debugf("basepath: %s", basePath) - searchHandler := &handler.GormRepository{} base := r.Group(basePath) { @@ -120,7 +119,7 @@ func (ae *AppEngine) Setup() (*gin.RouterGroup, *gin.Engine) { secure.POST("/resource/composition", handler.CreateResourceComposition) secure.POST("/resource/related", handler.CreateRelatedResources) secure.DELETE("/encounter/:encounterId/related/:resourceType/:resourceId", handler.EncounterUnlinkResource) - secure.GET("/resource/search", searchHandler.SearchResourcesHandler) + secure.GET("/resource/search", handler.SearchResourcesHandler) secure.GET("/dashboards", handler.GetDashboard) secure.POST("/dashboards", handler.AddDashboardLocation) From 0096cc4c26f0732a060ea4cf60709a791660976a Mon Sep 17 00:00:00 2001 From: "Theo@tsa.team" Date: Tue, 24 Jun 2025 17:01:07 +0300 Subject: [PATCH 003/126] feat: :sparkles: Add pagination and hits per page support on request handler [RECORD-SEARCH] --- backend/pkg/search/typesense_client.go | 20 +++++++++--- backend/pkg/web/handler/search.go | 43 ++++++++++++++------------ 2 files changed, 40 insertions(+), 23 deletions(-) diff --git a/backend/pkg/search/typesense_client.go b/backend/pkg/search/typesense_client.go index addc7320e..531344c5c 100644 --- a/backend/pkg/search/typesense_client.go +++ b/backend/pkg/search/typesense_client.go @@ -59,11 +59,13 @@ func (s *SearchClient) IndexResource(resource *models.ResourceBase) error { return nil } -func (s *SearchClient) SearchResources(query string, resourceTypeFilter *string) ([]map[string]interface{}, error) { +func (s *SearchClient) SearchResources(query string, resourceTypeFilter *string, page int, perPage int) ([]map[string]interface{}, int, error) { searchParams := &api.SearchCollectionParams{ Q: query, QueryBy: "sort_title,code_text,source_resource_type,source_resource_id,source_uri", SortBy: ptr("sort_date:desc"), + Page: intPtr(page), + PerPage: intPtr(perPage), } if resourceTypeFilter != nil { @@ -73,10 +75,9 @@ func (s *SearchClient) SearchResources(query string, resourceTypeFilter *string) resp, err := s.Client.Collection("resources").Documents().Search(context.Background(), searchParams) if err != nil { - return nil, err + return nil, 0, err } - // resp.Hits contains the documents matched results := make([]map[string]interface{}, 0, len(*resp.Hits)) for _, hit := range *resp.Hits { if hit.Document != nil { @@ -84,7 +85,13 @@ func (s *SearchClient) SearchResources(query string, resourceTypeFilter *string) } } - return results, nil + // Extract total number of matches + total := 0 + if resp.Found != nil { + total = *resp.Found + } + + return results, total, nil } func derefStr(s *string) string { @@ -107,3 +114,8 @@ func extractCodeText(raw map[string]interface{}) (string, bool) { func ptr(s string) *string { return &s } + +// intPtr returns a pointer to the given int. +func intPtr(i int) *int { + return &i +} diff --git a/backend/pkg/web/handler/search.go b/backend/pkg/web/handler/search.go index 1a44c942c..07b41e407 100644 --- a/backend/pkg/web/handler/search.go +++ b/backend/pkg/web/handler/search.go @@ -1,8 +1,8 @@ package handler import ( - "fmt" "net/http" + "strconv" "github.com/fastenhealth/fasten-onprem/backend/pkg/database" typesenseClient "github.com/fastenhealth/fasten-onprem/backend/pkg/search" @@ -15,7 +15,6 @@ type GormRepository struct { } func SearchResourcesHandler(c *gin.Context) { - // Get logger from context or use a default one logger := logrus.WithFields(logrus.Fields{ "handler": "SearchResourcesHandler", "path": c.Request.URL.Path, @@ -27,34 +26,33 @@ func SearchResourcesHandler(c *gin.Context) { query := c.Query("q") resourceTypeFilter := c.Query("type") + pageStr := c.DefaultQuery("page", "1") + perPageStr := c.DefaultQuery("per_page", "10") + + page, err := strconv.Atoi(pageStr) + if err != nil || page < 1 { + page = 1 + } + + perPage, err := strconv.Atoi(perPageStr) + if err != nil || perPage < 1 || perPage > 250 { + perPage = 10 + } + if query == "" { logger.Warn("Missing required query parameter 'q'") c.JSON(http.StatusBadRequest, gin.H{"error": "query parameter 'q' is required"}) return } - logger.Debug("Checking Typesense client availability") if typesenseClient.Client == nil { logger.Error("Typesense client is not initialized") c.JSON(http.StatusServiceUnavailable, gin.H{"error": "search service unavailable"}) return } - logger.Debug("Creating search client") searchClient := &typesenseClient.SearchClient{Client: typesenseClient.Client} - if searchClient == nil { - logger.Error("Failed to create search client") - c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"}) - return - } - - logger.WithFields(logrus.Fields{ - "query": query, - "type": resourceTypeFilter, - }).Info("Executing search query") - - // Only pass the filter if it's not empty var filterPtr *string if resourceTypeFilter != "" { filterPtr = &resourceTypeFilter @@ -63,7 +61,7 @@ func SearchResourcesHandler(c *gin.Context) { logger.Debug("No resource type filter provided - searching all types") } - results, err := searchClient.SearchResources(query, filterPtr) + results, total, err := searchClient.SearchResources(query, filterPtr, page, perPage) if err != nil { logger.WithError(err).Error("Search query failed") c.JSON(http.StatusInternalServerError, gin.H{"error": "internal server error"}) @@ -72,8 +70,15 @@ func SearchResourcesHandler(c *gin.Context) { logger.WithFields(logrus.Fields{ "resultCount": len(results), - "resultType": fmt.Sprintf("%T", results), + "totalFound": total, + "page": page, + "perPage": perPage, }).Info("Search query completed successfully") - c.JSON(http.StatusOK, results) + c.JSON(http.StatusOK, gin.H{ + "results": results, + "total": total, + "page": page, + "per_page": perPage, + }) } From ac1da8cc3ee9d5769b29ab2b955ae5a77d390049 Mon Sep 17 00:00:00 2001 From: "Theo@tsa.team" Date: Wed, 25 Jun 2025 16:17:33 +0300 Subject: [PATCH 004/126] feat: :sparkles: Initial UI and route for resource search, integrate BE call [RECORD-SEARCH] --- backend/pkg/web/handler/search.go | 8 +- frontend/src/app/app-routing.module.ts | 2 + frontend/src/app/app.module.ts | 2 + .../typesense/typesense-result-model.ts | 18 ++++ .../resource-search.component.html | 69 +++++++++++++ .../resource-search.component.scss | 96 ++++++++++++++++++ .../resource-search.component.spec.ts | 23 +++++ .../resource-search.component.ts | 97 +++++++++++++++++++ .../src/app/services/fasten-api.service.ts | 32 ++++++ .../src/assets/scss/dark/_components.scss | 5 + 10 files changed, 345 insertions(+), 7 deletions(-) create mode 100644 frontend/src/app/models/typesense/typesense-result-model.ts create mode 100644 frontend/src/app/pages/resource-search/resource-search.component.html create mode 100644 frontend/src/app/pages/resource-search/resource-search.component.scss create mode 100644 frontend/src/app/pages/resource-search/resource-search.component.spec.ts create mode 100644 frontend/src/app/pages/resource-search/resource-search.component.ts diff --git a/backend/pkg/web/handler/search.go b/backend/pkg/web/handler/search.go index 07b41e407..ea0763459 100644 --- a/backend/pkg/web/handler/search.go +++ b/backend/pkg/web/handler/search.go @@ -24,7 +24,7 @@ func SearchResourcesHandler(c *gin.Context) { logger.Info("Search request started") - query := c.Query("q") + query := c.DefaultQuery("q", "*") // Default to wildcard search if no query is provided resourceTypeFilter := c.Query("type") pageStr := c.DefaultQuery("page", "1") perPageStr := c.DefaultQuery("per_page", "10") @@ -39,12 +39,6 @@ func SearchResourcesHandler(c *gin.Context) { perPage = 10 } - if query == "" { - logger.Warn("Missing required query parameter 'q'") - c.JSON(http.StatusBadRequest, gin.H{"error": "query parameter 'q' is required"}) - return - } - if typesenseClient.Client == nil { logger.Error("Typesense client is not initialized") c.JSON(http.StatusServiceUnavailable, gin.H{"error": "search service unavailable"}) diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index d0e257f15..4437202b0 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -22,6 +22,7 @@ import { ResourceDetailComponent } from './pages/resource-detail/resource-detail import { SourceDetailComponent } from './pages/source-detail/source-detail.component'; import { UserCreateComponent } from './pages/user-create/user-create.component'; import { UserListComponent } from './pages/user-list/user-list.component'; +import { ResourceSearchComponent } from "./pages/resource-search/resource-search.component"; const routes: Routes = [ @@ -44,6 +45,7 @@ const routes: Routes = [ { path: 'sources', component: MedicalSourcesComponent, canActivate: [ IsAuthenticatedAuthGuard] }, { path: 'sources/callback/:state', component: MedicalSourcesComponent, canActivate: [ IsAuthenticatedAuthGuard] }, { path: 'resource/create', component: ResourceCreatorComponent, canActivate: [ IsAuthenticatedAuthGuard] }, + { path: 'resource/search', component: ResourceSearchComponent, canActivate: [ IsAuthenticatedAuthGuard] }, { path: 'desktop/callback/:state', component: DesktopCallbackComponent, canActivate: [ IsAuthenticatedAuthGuard] }, diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index 9efff0168..1ee1bafe7 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -41,6 +41,7 @@ import { AuthSignupWizardComponent } from './pages/auth-signup-wizard/auth-signu import {ShowFirstRunWizardGuard} from './auth-guards/show-first-run-wizard-guard'; import { IconsModule } from './icon-module'; import { UserListComponent } from './pages/user-list/user-list.component'; +import { ResourceSearchComponent } from './pages/resource-search/resource-search.component'; @NgModule({ declarations: [ @@ -62,6 +63,7 @@ import { UserListComponent } from './pages/user-list/user-list.component'; BackgroundJobsComponent, AuthSignupWizardComponent, UserListComponent, + ResourceSearchComponent, ], imports: [ FormsModule, diff --git a/frontend/src/app/models/typesense/typesense-result-model.ts b/frontend/src/app/models/typesense/typesense-result-model.ts new file mode 100644 index 000000000..03be95897 --- /dev/null +++ b/frontend/src/app/models/typesense/typesense-result-model.ts @@ -0,0 +1,18 @@ +export interface TypesenseDocument { + id: string; + sort_title: string; + source_uri: string; + user_id: string; + source_id: string; + source_resource_type: string; + source_resource_id: string; + sort_date: number; + resource_raw: any; +} + +export interface TypesenseSearchResponse { + results: TypesenseDocument[]; + total: number; + page: number; + per_page: number; +} diff --git a/frontend/src/app/pages/resource-search/resource-search.component.html b/frontend/src/app/pages/resource-search/resource-search.component.html new file mode 100644 index 000000000..6a9881a5e --- /dev/null +++ b/frontend/src/app/pages/resource-search/resource-search.component.html @@ -0,0 +1,69 @@ +
+
+
+ + +
+
+ + +
+ + +
+ + +
+
+
+
{{ doc.sort_title || 'Untitled' }}
+

Type: {{ doc.source_resource_type }}

+

Date: {{ doc.sort_date || 'N/A' }}

+
+
+
+ + +
+
+ + + + Total results: {{ totalResults }} +
+ +
+ Page {{ page }} of {{ totalPages }} + +
+
+
+
+
\ No newline at end of file diff --git a/frontend/src/app/pages/resource-search/resource-search.component.scss b/frontend/src/app/pages/resource-search/resource-search.component.scss new file mode 100644 index 000000000..a2e3346f7 --- /dev/null +++ b/frontend/src/app/pages/resource-search/resource-search.component.scss @@ -0,0 +1,96 @@ +:host { + display: flex; + flex-direction: column; + gap: 1rem; + padding: 1rem; +} + +.search-controls { + display: flex; + gap: 1rem; + align-items: center; + + input[type="text"] { + flex: 1; + padding: 0.5rem; + border: 1px solid #ccc; + border-radius: 4px; + } + + select { + padding: 0.5rem; + border-radius: 4px; + } +} + +.results-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 1rem; +} + +.result-card { + background-color: #ccc; + color: #fff; + padding: 2rem; + border-radius: 8px; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15); + + h3 { + margin-top: 0; + } + + button { + margin-top: 1rem; + background-color: transparent; + border: 1px solid var(--dark-alert-text-color); + color: var(--dark-alert-text-color); + padding: 0.4rem 0.8rem; + border-radius: 4px; + cursor: pointer; + + &:hover { + background-color: var(--dark-alert-text-color); + color: var(--dark-alert-background-color); + } + } +} + +.pagination-controls { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 1rem; + + .per-page { + display: flex; + align-items: center; + gap: 0.5rem; + } + + .pagination { + display: flex; + align-items: center; + gap: 1rem; + + button { + padding: 0.4rem 0.8rem; + border-radius: 4px; + border: none; + background-color: var(--dark-alert-text-color); + color: var(--dark-alert-background-color); + cursor: pointer; + + &:disabled { + opacity: 0.4; + cursor: not-allowed; + } + } + } +} + +.full-width-link { + width: 100%; + text-align: center; +} diff --git a/frontend/src/app/pages/resource-search/resource-search.component.spec.ts b/frontend/src/app/pages/resource-search/resource-search.component.spec.ts new file mode 100644 index 000000000..e7f61a6e0 --- /dev/null +++ b/frontend/src/app/pages/resource-search/resource-search.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ResourceSearchComponent } from './resource-search.component'; + +describe('ResourceSearchComponent', () => { + let component: ResourceSearchComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ResourceSearchComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ResourceSearchComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/pages/resource-search/resource-search.component.ts b/frontend/src/app/pages/resource-search/resource-search.component.ts new file mode 100644 index 000000000..732c4a41a --- /dev/null +++ b/frontend/src/app/pages/resource-search/resource-search.component.ts @@ -0,0 +1,97 @@ +import { Component, OnInit } from '@angular/core'; +import { FastenApiService } from '../../services/fasten-api.service'; +import { + TypesenseDocument, + TypesenseSearchResponse, +} from '../../models/typesense/typesense-result-model'; + +@Component({ + selector: 'resource-search', + templateUrl: './resource-search.component.html', + styleUrls: ['./resource-search.component.scss'], +}) +export class ResourceSearchComponent implements OnInit { + searchQuery: string = ''; + selectedResourceType: string = ''; + page: number = 1; + perPage: number = 10; + + totalResults: number = 0; + results: TypesenseDocument[] = []; + + supportedTypes: string[] = [ + 'Appointment', + 'AllergyIntolerance', + 'AdverseEvent', + 'Binary', + 'CarePlan', + 'CareTeam', + 'Communication', + 'Condition', + 'Coverage', + 'Device', + 'DeviceRequest', + 'DiagnosticReport', + 'DocumentReference', + 'Encounter', + 'Goal', + 'Immunization', + 'Location', + 'Medication', + 'MedicationAdministration', + 'MedicationDispense', + 'MedicationRequest', + 'NutritionOrder', + 'Observation', + 'Organization', + 'Patient', + 'Practitioner', + 'Procedure', + 'ServiceRequest', + ]; + + constructor(private fastenApi: FastenApiService) {} + + ngOnInit(): void { + this.search(); + } + + viewResource(doc: TypesenseDocument): void { + console.log('Selected document:', doc); + // Next: either show in modal or route to a detail page with the JSON + } + + search(): void { + this.fastenApi + .searchResources({ + query: this.searchQuery, + resourceType: this.selectedResourceType, + page: this.page, + per_page: this.perPage, + }) + .subscribe((response: TypesenseSearchResponse) => { + this.results = response.results.map((doc: any) => ({ + ...doc, + resource_raw: + typeof doc.resource_raw === 'string' + ? JSON.parse(doc.resource_raw) + : doc.resource_raw, + })); + this.totalResults = response.total; + }); + } + + onPageChange(newPage: number): void { + this.page = newPage; + this.search(); + } + + get totalPages(): number { + return Math.ceil(this.totalResults / this.perPage); + } + + onPerPageChange(): void { + this.page = 1; + this.search(); + } +} diff --git a/frontend/src/app/services/fasten-api.service.ts b/frontend/src/app/services/fasten-api.service.ts index 3f71bbaaf..d15f4e326 100644 --- a/frontend/src/app/services/fasten-api.service.ts +++ b/frontend/src/app/services/fasten-api.service.ts @@ -29,6 +29,7 @@ import { } from 'fhir/r4'; import {FormRequestHealthSystem} from '../models/fasten/form-request-health-system'; import { UpdateResourcePayload } from '../models/fasten/resource_update'; +import { TypesenseSearchResponse } from '../models/typesense/typesense-result-model'; @Injectable({ providedIn: 'root' }) @@ -96,6 +97,37 @@ export class FastenApiService { ); } + searchResources(params: { + query?: string; + resourceType?: string; + page?: number; + per_page?: number; + }): Observable { + let queryParams = {}; + if (params.query) { + queryParams['q'] = params.query; + } + if (params.page !== undefined) { + queryParams['page'] = params.page; + } + if (params.per_page !== undefined) { + queryParams['per_page'] = params.per_page; + } + if (params.resourceType) { + queryParams['resource_type'] = params.resourceType; + } + + return this._httpClient.get< + TypesenseSearchResponse + >( + `${GetEndpointAbsolutePath( + globalThis.location, + environment.fasten_api_endpoint_base + )}/secure/resource/search`, + { params: queryParams } + ); + } + getSummary(): Observable { return this._httpClient.get(`${GetEndpointAbsolutePath(globalThis.location, environment.fasten_api_endpoint_base)}/secure/summary`, ) .pipe( diff --git a/frontend/src/assets/scss/dark/_components.scss b/frontend/src/assets/scss/dark/_components.scss index 144c7af27..9e7601a84 100644 --- a/frontend/src/assets/scss/dark/_components.scss +++ b/frontend/src/assets/scss/dark/_components.scss @@ -11,6 +11,11 @@ border-color: var(--border); } + .result-card { + background-color: var(--card); + color: var(--dark-text-color); + } + .card-footer { border-color: var(--border); } From ec161adc778695af0f9fd8c97c6588e61b5a92ff Mon Sep 17 00:00:00 2001 From: "Vadim @ TSA" Date: Wed, 25 Jun 2025 18:44:53 +0300 Subject: [PATCH 005/126] chore: use the latest versions of typesense and typesense-go --- backend/pkg/search/typesense.go | 4 +-- backend/pkg/search/typesense_client.go | 10 +++--- docker-compose.yml | 37 +++++++++++----------- go.mod | 22 +++++++------ go.sum | 44 +++++++++++++++----------- 5 files changed, 62 insertions(+), 55 deletions(-) diff --git a/backend/pkg/search/typesense.go b/backend/pkg/search/typesense.go index e8a6397bb..b91922302 100644 --- a/backend/pkg/search/typesense.go +++ b/backend/pkg/search/typesense.go @@ -8,8 +8,8 @@ import ( "time" "github.com/sirupsen/logrus" - "github.com/typesense/typesense-go/typesense" - "github.com/typesense/typesense-go/typesense/api" + "github.com/typesense/typesense-go/v3/typesense" + "github.com/typesense/typesense-go/v3/typesense/api" ) var Client *typesense.Client diff --git a/backend/pkg/search/typesense_client.go b/backend/pkg/search/typesense_client.go index 531344c5c..ecc179624 100644 --- a/backend/pkg/search/typesense_client.go +++ b/backend/pkg/search/typesense_client.go @@ -8,8 +8,8 @@ import ( "github.com/fastenhealth/fasten-onprem/backend/pkg/models" "github.com/google/uuid" - "github.com/typesense/typesense-go/typesense" - "github.com/typesense/typesense-go/typesense/api" + "github.com/typesense/typesense-go/v3/typesense" + "github.com/typesense/typesense-go/v3/typesense/api" ) type SearchClient struct { @@ -51,7 +51,7 @@ func (s *SearchClient) IndexResource(resource *models.ResourceBase) error { } } - _, err := s.Client.Collection("resources").Documents().Upsert(context.Background(), doc) + _, err := s.Client.Collection("resources").Documents().Upsert(context.Background(), doc, nil) if err != nil { return fmt.Errorf("failed to index resource: %w", err) } @@ -61,8 +61,8 @@ func (s *SearchClient) IndexResource(resource *models.ResourceBase) error { func (s *SearchClient) SearchResources(query string, resourceTypeFilter *string, page int, perPage int) ([]map[string]interface{}, int, error) { searchParams := &api.SearchCollectionParams{ - Q: query, - QueryBy: "sort_title,code_text,source_resource_type,source_resource_id,source_uri", + Q: ptr(query), + QueryBy: ptr("sort_title,code_text,source_resource_type,source_resource_id,source_uri"), SortBy: ptr("sort_date:desc"), Page: intPtr(page), PerPage: intPtr(perPage), diff --git a/docker-compose.yml b/docker-compose.yml index 1bb46802a..2b3315916 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,25 +1,24 @@ version: "3.9" services: - fasten: - # NOTE: only developers need to build Fasten from source - # For production, use the pre-built image from GitHub Packages - # ghcr.io/fastenhealth/fasten-onprem:main - build: - context: . - dockerfile: Dockerfile - args: -# - FASTEN_ENV=prod - - FASTEN_ENV=sandbox - ports: - - "9090:8080" - volumes: - - ./db:/opt/fasten/db -# - ./config.example.yaml:/opt/fasten/config/config.yaml - environment: - - TYPESENSE_API_KEY=xyz123 +# fasten: +# # NOTE: only developers need to build Fasten from source +# # For production, use the pre-built image from GitHub Packages +# # ghcr.io/fastenhealth/fasten-onprem:main +# build: +# context: . +# dockerfile: Dockerfile +# args: +# # - FASTEN_ENV=prod +# - FASTEN_ENV=sandbox +# ports: +# - "9090:8080" +# volumes: +# - ./db:/opt/fasten/db +# # - ./config.example.yaml:/opt/fasten/config/config.yaml +# environment: +# - TYPESENSE_API_KEY=xyz123 typesense: - image: typesense/typesense:0.25.1 - container_name: typesense + image: typesense/typesense:28.0 ports: - "8108:8108" volumes: diff --git a/go.mod b/go.mod index 09763c510..1ac4b1054 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module github.com/fastenhealth/fasten-onprem -go 1.18 +go 1.22 + +toolchain go1.22.12 //replace github.com/fastenhealth/fasten-sources => ../fasten-sources @@ -22,19 +24,19 @@ require ( github.com/golang-jwt/jwt/v4 v4.4.2 github.com/golang/mock v1.6.0 github.com/google/go-github/v54 v54.0.0 - github.com/google/uuid v1.5.0 + github.com/google/uuid v1.6.0 github.com/iancoleman/strcase v0.2.0 github.com/lestrrat-go/jwx/v2 v2.0.11 github.com/samber/lo v1.35.0 github.com/sirupsen/logrus v1.9.0 github.com/spf13/viper v1.12.0 github.com/stretchr/testify v1.9.0 - github.com/typesense/typesense-go v1.1.0 + github.com/typesense/typesense-go/v3 v3.2.0 github.com/urfave/cli/v2 v2.11.2 - golang.org/x/crypto v0.23.0 + golang.org/x/crypto v0.24.0 golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 - golang.org/x/mod v0.17.0 - golang.org/x/net v0.25.0 + golang.org/x/mod v0.18.0 + golang.org/x/net v0.26.0 gorm.io/datatypes v1.0.7 gorm.io/driver/sqlite v1.5.4 gorm.io/gorm v1.25.4 @@ -49,7 +51,7 @@ require ( github.com/jackc/pgx/v5 v5.4.3 // indirect github.com/mattn/go-sqlite3 v1.14.17 // indirect github.com/oapi-codegen/runtime v1.1.1 // indirect - github.com/sony/gobreaker v0.5.0 // indirect + github.com/sony/gobreaker v1.0.0 // indirect ) require ( @@ -108,9 +110,9 @@ require ( github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect golang.org/x/arch v0.4.0 // indirect golang.org/x/oauth2 v0.11.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/term v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/term v0.21.0 // indirect + golang.org/x/text v0.18.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index c3902e5bb..d47f20430 100644 --- a/go.sum +++ b/go.sum @@ -117,6 +117,7 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= @@ -133,6 +134,7 @@ github.com/go-gormigrate/gormigrate/v2 v2.1.1/go.mod h1:L7nJ620PFDKei9QOhJzqA8kR github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= @@ -197,6 +199,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github/v54 v54.0.0 h1:OZdXwow4EAD5jEo5qg+dGFH2DpkyZvVsAehjvJuUL/c= github.com/google/go-github/v54 v54.0.0/go.mod h1:Sw1LXWHhXRZtzJ9LI5fyJg9wbQzYvFhW8W5P2yaAQ7s= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= @@ -219,8 +222,8 @@ github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= -github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= @@ -279,6 +282,7 @@ github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv github.com/jgiannuzzi/go-sqlite3 v1.14.17-0.20230719111531-6e53453ccbd3 h1:Iqr8ZWwijosAcawoD9IZ7cwj61WH50Rysm+RQ+ZIB4I= github.com/jgiannuzzi/go-sqlite3 v1.14.17-0.20230719111531-6e53453ccbd3/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/jinzhu/copier v0.3.4 h1:mfU6jI9PtCeUjkjQ322dlff9ELjGDu975C2p/nrubVI= +github.com/jinzhu/copier v0.3.4/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= @@ -389,8 +393,8 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= -github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sony/gobreaker v1.0.0 h1:feX5fGGXSl3dYd4aHZItw+FpHLvvoaqkawKjVNiFMNQ= +github.com/sony/gobreaker v1.0.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= @@ -423,10 +427,11 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8 github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI= github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= +github.com/thoas/go-funk v0.9.1/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/typesense/typesense-go v1.1.0 h1:QocehDarVXRArMIosPIdawiVFZZbnRkPJxwnAGOFkzw= -github.com/typesense/typesense-go v1.1.0/go.mod h1:KcPODU7ltrcUFC/gygMTkAAfZ9M8/q6ayrdl1MnE1kI= +github.com/typesense/typesense-go/v3 v3.2.0 h1:pmrq46PkxhS0xowPMCnfOcBYCbIfMESas45py4jk8LQ= +github.com/typesense/typesense-go/v3 v3.2.0/go.mod h1:Jx4PAXe3jRx6sc032nhN9Aj+OvMoPtQJW6p1a6H4Zeg= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/urfave/cli/v2 v2.11.2 h1:FVfNg4m3vbjbBpLYxW//WjxUoHvJ9TlppXcqY9Q9ZfA= @@ -450,7 +455,8 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= @@ -478,8 +484,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -518,8 +524,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -558,8 +564,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -640,15 +646,15 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -660,8 +666,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 113804e93cd84390edd7ae54f3280674edba7ddf Mon Sep 17 00:00:00 2001 From: "Vadim @ TSA" Date: Wed, 25 Jun 2025 18:45:44 +0300 Subject: [PATCH 006/126] chore: revert fasten service --- docker-compose.yml | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 2b3315916..3885ae4aa 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,22 +1,22 @@ version: "3.9" services: -# fasten: -# # NOTE: only developers need to build Fasten from source -# # For production, use the pre-built image from GitHub Packages -# # ghcr.io/fastenhealth/fasten-onprem:main -# build: -# context: . -# dockerfile: Dockerfile -# args: -# # - FASTEN_ENV=prod -# - FASTEN_ENV=sandbox -# ports: -# - "9090:8080" -# volumes: -# - ./db:/opt/fasten/db -# # - ./config.example.yaml:/opt/fasten/config/config.yaml -# environment: -# - TYPESENSE_API_KEY=xyz123 + fasten: + # NOTE: only developers need to build Fasten from source + # For production, use the pre-built image from GitHub Packages + # ghcr.io/fastenhealth/fasten-onprem:main + build: + context: . + dockerfile: Dockerfile + args: +# - FASTEN_ENV=prod + - FASTEN_ENV=sandbox + ports: + - "9090:8080" + volumes: + - ./db:/opt/fasten/db +# - ./config.example.yaml:/opt/fasten/config/config.yaml + environment: + - TYPESENSE_API_KEY=xyz123 typesense: image: typesense/typesense:28.0 ports: From 4b967be84ba365deccac3464c7be2db5bf71e6b4 Mon Sep 17 00:00:00 2001 From: "Theo@tsa.team" Date: Thu, 26 Jun 2025 10:30:53 +0300 Subject: [PATCH 007/126] feat: :sparkles: Fix filtering by type --- .../pages/resource-search/resource-search.component.html | 2 +- .../pages/resource-search/resource-search.component.scss | 5 ----- .../app/pages/resource-search/resource-search.component.ts | 2 +- frontend/src/app/services/fasten-api.service.ts | 6 +++--- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/frontend/src/app/pages/resource-search/resource-search.component.html b/frontend/src/app/pages/resource-search/resource-search.component.html index 6a9881a5e..4f783aea8 100644 --- a/frontend/src/app/pages/resource-search/resource-search.component.html +++ b/frontend/src/app/pages/resource-search/resource-search.component.html @@ -54,7 +54,7 @@
{{ doc.sort_title || 'Untitled' }}
Page {{ page }} of {{ totalPages }} @@ -22,8 +22,8 @@
diff --git a/frontend/src/app/components/report-labs-observation/report-labs-observation.component.html b/frontend/src/app/components/report-labs-observation/report-labs-observation.component.html index c4e8b438c..afc59a27f 100644 --- a/frontend/src/app/components/report-labs-observation/report-labs-observation.component.html +++ b/frontend/src/app/components/report-labs-observation/report-labs-observation.component.html @@ -17,7 +17,7 @@
-
+

Short Name: {{firstObservation | fhirPath: "Observation.code.text"}}
diff --git a/frontend/src/app/components/report-medical-history-condition/report-medical-history-condition.component.html b/frontend/src/app/components/report-medical-history-condition/report-medical-history-condition.component.html index ca5c51ca9..23be9d4af 100644 --- a/frontend/src/app/components/report-medical-history-condition/report-medical-history-condition.component.html +++ b/frontend/src/app/components/report-medical-history-condition/report-medical-history-condition.component.html @@ -17,7 +17,7 @@

-
+

Involved in Care

diff --git a/frontend/src/app/components/report-medical-history-explanation-of-benefit/report-medical-history-explanation-of-benefit.component.html b/frontend/src/app/components/report-medical-history-explanation-of-benefit/report-medical-history-explanation-of-benefit.component.html index 190699c82..b2468ddaf 100644 --- a/frontend/src/app/components/report-medical-history-explanation-of-benefit/report-medical-history-explanation-of-benefit.component.html +++ b/frontend/src/app/components/report-medical-history-explanation-of-benefit/report-medical-history-explanation-of-benefit.component.html @@ -17,7 +17,7 @@
-
+

Involved in Care

diff --git a/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.html b/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.html index 90fb8d3ef..84317adda 100644 --- a/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.html +++ b/frontend/src/app/components/report-medical-history-timeline-panel/report-medical-history-timeline-panel.component.html @@ -103,7 +103,7 @@