Skip to content

Commit 9f13a67

Browse files
Merge pull request #16 from mauriciozanettisalomao/feat/lfxv2-199-org-search-suggestions
[LFX-V2-199] Query Service: org search - suggestions endpoint
2 parents 96e5945 + 61fab90 commit 9f13a67

File tree

35 files changed

+1325
-49
lines changed

35 files changed

+1325
-49
lines changed

charts/lfx-v2-query-service/Chart.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ apiVersion: v2
55
name: lfx-v2-query-service
66
description: LFX Platform V2 Query Service chart
77
type: application
8-
version: 0.4.0
8+
version: 0.4.1
99
appVersion: "latest"

charts/lfx-v2-query-service/templates/httproute.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ spec:
1515
rules:
1616
- matches:
1717
- path:
18-
type: Exact
18+
type: PathPrefix
1919
value: /query/orgs
2020
{{- if .Values.heimdall.enabled }}
2121
filters:
@@ -25,7 +25,7 @@ spec:
2525
kind: Middleware
2626
name: heimdall
2727
{{- if .Values.orgSearch.rateLimit.enabled }}
28-
# Organization search endpoint (with rate limiting + authentication)
28+
# Organization endpoints (with rate limiting + authentication)
2929
- type: ExtensionRef
3030
extensionRef:
3131
group: traefik.io

charts/lfx-v2-query-service/templates/ruleset.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,19 @@ spec:
6161
config:
6262
values:
6363
aud: lfx-v2-query-service
64+
- id: "rule:lfx:lfx-v2-query-service:org-suggest"
65+
match:
66+
methods:
67+
- GET
68+
routes:
69+
- path: /query/orgs/suggest
70+
execute:
71+
- authenticator: oidc
72+
{{- if .Values.app.use_oidc_contextualizer }}
73+
- contextualizer: oidc_contextualizer
74+
{{- end }}
75+
- authorizer: allow_all
76+
- finalizer: create_jwt
77+
config:
78+
values:
79+
aud: lfx-v2-query-service

cmd/service/converters.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,31 @@ func (s *querySvcsrvc) domainOrganizationToResponse(org *model.Organization) *qu
9898
Employees: &org.Employees,
9999
}
100100
}
101+
102+
// payloadToOrganizationSuggestionCriteria converts the generated payload to domain organization suggestion criteria
103+
func (s *querySvcsrvc) payloadToOrganizationSuggestionCriteria(ctx context.Context, p *querysvc.SuggestOrgsPayload) model.OrganizationSuggestionCriteria {
104+
criteria := model.OrganizationSuggestionCriteria{
105+
Query: p.Query,
106+
}
107+
return criteria
108+
}
109+
110+
// domainOrganizationSuggestionsToResponse converts domain organization suggestions result to generated response
111+
func (s *querySvcsrvc) domainOrganizationSuggestionsToResponse(result *model.OrganizationSuggestionsResult) *querysvc.SuggestOrgsResult {
112+
if result == nil || len(result.Suggestions) == 0 {
113+
return &querysvc.SuggestOrgsResult{Suggestions: []*querysvc.OrganizationSuggestion{}}
114+
}
115+
suggestions := make([]*querysvc.OrganizationSuggestion, len(result.Suggestions))
116+
117+
for i, domainSuggestion := range result.Suggestions {
118+
suggestions[i] = &querysvc.OrganizationSuggestion{
119+
Name: domainSuggestion.Name,
120+
Domain: domainSuggestion.Domain,
121+
Logo: domainSuggestion.Logo,
122+
}
123+
}
124+
125+
return &querysvc.SuggestOrgsResult{
126+
Suggestions: suggestions,
127+
}
128+
}

cmd/service/providers.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ func OrganizationSearcherImpl(ctx context.Context) port.OrganizationSearcher {
166166
// Parse Clearbit environment variables
167167
clearbitAPIKey := os.Getenv("CLEARBIT_CREDENTIAL")
168168
clearbitBaseURL := os.Getenv("CLEARBIT_BASE_URL")
169+
clearbitAutocompleteBaseURL := os.Getenv("CLEARBIT_AUTOCOMPLETE_BASE_URL")
169170
clearbitTimeout := os.Getenv("CLEARBIT_TIMEOUT")
170171

171172
clearbitMaxRetries := os.Getenv("CLEARBIT_MAX_RETRIES")
@@ -181,6 +182,7 @@ func OrganizationSearcherImpl(ctx context.Context) port.OrganizationSearcher {
181182

182183
clearbitConfig, err := clearbit.NewConfig(clearbitAPIKey,
183184
clearbitBaseURL,
185+
clearbitAutocompleteBaseURL,
184186
clearbitTimeout,
185187
clearbitMaxRetriesInt,
186188
clearbitRetryDelay,
@@ -191,6 +193,7 @@ func OrganizationSearcherImpl(ctx context.Context) port.OrganizationSearcher {
191193

192194
slog.InfoContext(ctx, "initializing Clearbit organization searcher",
193195
"base_url", clearbitConfig.BaseURL,
196+
"autocomplete_base_url", clearbitConfig.AutocompleteBaseURL,
194197
"timeout", clearbitConfig.Timeout,
195198
"max_retries", clearbitConfig.MaxRetries,
196199
)

cmd/service/service.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,27 @@ func (s *querySvcsrvc) QueryOrgs(ctx context.Context, p *querysvc.QueryOrgsPaylo
8787
return res, nil
8888
}
8989

90+
// Get organization suggestions for typeahead search based on a query.
91+
func (s *querySvcsrvc) SuggestOrgs(ctx context.Context, p *querysvc.SuggestOrgsPayload) (res *querysvc.SuggestOrgsResult, err error) {
92+
93+
slog.DebugContext(ctx, "querySvc.suggest-orgs",
94+
"query", p.Query,
95+
)
96+
97+
// Convert payload to domain criteria
98+
criteria := s.payloadToOrganizationSuggestionCriteria(ctx, p)
99+
100+
// Execute search using the service layer
101+
result, errSuggestOrgs := s.organizationService.SuggestOrganizations(ctx, criteria)
102+
if errSuggestOrgs != nil {
103+
return nil, wrapError(ctx, errSuggestOrgs)
104+
}
105+
106+
// Convert domain result to response
107+
res = s.domainOrganizationSuggestionsToResponse(result)
108+
return res, nil
109+
}
110+
90111
// Check if the service is able to take inbound requests.
91112
func (s *querySvcsrvc) Readyz(ctx context.Context) (res []byte, err error) {
92113
errIsReady := s.resourceService.IsReady(ctx)

design/query-svc.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,44 @@ var _ = dsl.Service("query-svc", func() {
127127
})
128128
})
129129

130+
dsl.Method("suggest-orgs", func() {
131+
dsl.Description("Get organization suggestions for typeahead search based on a query.")
132+
133+
dsl.Security(JWTAuth)
134+
135+
dsl.Payload(func() {
136+
dsl.Token("bearer_token", dsl.String, func() {
137+
dsl.Description("Token")
138+
dsl.Example("eyJhbGci...")
139+
})
140+
dsl.Attribute("version", dsl.String, "Version of the API", func() {
141+
dsl.Enum("1")
142+
dsl.Example("1")
143+
})
144+
dsl.Attribute("query", dsl.String, "Search query for organization suggestions", func() {
145+
dsl.Example("linux")
146+
dsl.MinLength(1)
147+
})
148+
dsl.Required("bearer_token", "version", "query")
149+
})
150+
151+
dsl.Result(func() {
152+
dsl.Attribute("suggestions", dsl.ArrayOf(OrganizationSuggestion), "Organization suggestions", func() {})
153+
dsl.Required("suggestions")
154+
})
155+
156+
dsl.HTTP(func() {
157+
dsl.GET("/query/orgs/suggest")
158+
dsl.Param("version:v")
159+
dsl.Param("query")
160+
dsl.Header("bearer_token:Authorization")
161+
dsl.Response(dsl.StatusOK)
162+
dsl.Response("BadRequest", dsl.StatusBadRequest)
163+
dsl.Response("InternalServerError", dsl.StatusInternalServerError)
164+
dsl.Response("ServiceUnavailable", dsl.StatusServiceUnavailable)
165+
})
166+
})
167+
130168
dsl.Method("readyz", func() {
131169
dsl.Description("Check if the service is able to take inbound requests.")
132170
dsl.Meta("swagger:generate", "false")

design/types.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,21 @@ var Organization = dsl.Type("Organization", func() {
100100
})
101101
})
102102

103+
var OrganizationSuggestion = dsl.Type("OrganizationSuggestion", func() {
104+
dsl.Description("An organization suggestion for the search.")
105+
106+
dsl.Attribute("name", dsl.String, "Organization name", func() {
107+
dsl.Example("Linux Foundation")
108+
})
109+
dsl.Attribute("domain", dsl.String, "Organization domain", func() {
110+
dsl.Example("linuxfoundation.org")
111+
})
112+
dsl.Attribute("logo", dsl.String, "Organization logo URL", func() {
113+
dsl.Example("https://example.com/logo.png")
114+
})
115+
dsl.Required("name", "domain")
116+
})
117+
103118
// Define an example cached LFX resource for the nested "data" attribute for
104119
// resource searches. This example happens to be a committee to match the
105120
// example value of "committee" for the "type" attribute of Resource.

gen/http/cli/lfx_v2_query_service/cli.go

Lines changed: 27 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gen/http/openapi.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)