This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
# Install dependencies and development tools
make setup
make deps
# Generate API code from Goa design (required after design changes)
make apigen
# Or directly: goa gen github.com/linuxfoundation/lfx-v2-query-service/design
# Build the application
make build
# Run locally with mock implementations (development)
SEARCH_SOURCE=mock ACCESS_CONTROL_SOURCE=mock go run ./cmd
# Run with OpenSearch and NATS (production-like)
SEARCH_SOURCE=opensearch ACCESS_CONTROL_SOURCE=nats \
OPENSEARCH_URL=http://localhost:9200 \
OPENSEARCH_INDEX=resources \
NATS_URL=nats://localhost:4222 \
go run ./cmd# Run tests
make test
# Or: go test -v -race -coverprofile=coverage.out ./...
# Run linting
make lint
# Or: golangci-lint run ./...
# Run specific test
go test -v -run TestResourceSearch ./internal/service/# Build Docker image
make docker-build
# Run Docker container
make docker-runThis service follows clean architecture principles with clear separation of concerns:
-
Domain Layer (
internal/domain/)model/: Core business entities (Resource, SearchCriteria, AccessCheck)port/: Interfaces defining contracts (ResourceSearcher, AccessControlChecker)
-
Service Layer (
internal/service/)- Business logic orchestration
- Coordinates between domain and infrastructure
-
Infrastructure Layer (
internal/infrastructure/)opensearch/: OpenSearch implementation for resource searchnats/: NATS implementation for access controlmock/: Mock implementations for testing
-
Presentation Layer (
gen/,cmd/)- Generated Goa code for HTTP endpoints
- Service implementation connecting Goa to domain logic
- Dependency Injection: Concrete implementations injected in
cmd/main.go - Port/Adapter Pattern: Domain interfaces (ports) with swappable implementations
- Repository Pattern: Search and access control abstracted behind interfaces
- Design specifications in
design/directory - Generated code in
gen/(DO NOT manually edit) - After design changes, always run
make apigen
- HTTP request → Goa generated server (
gen/http/query_svc/server/) - Service layer (
cmd/query_svc/query_svc.go) - Use case orchestration (
internal/service/resource_search.go) - Domain interfaces called with concrete implementations
- Response formatted and returned through Goa
Environment variables control implementation selection:
SEARCH_SOURCE: "mock" or "opensearch"ACCESS_CONTROL_SOURCE: "mock" or "nats"- Additional configs for OpenSearch and NATS connections
- Unit tests use mock implementations
- Integration tests can switch between real and mock implementations
- Test files follow
*_test.gopattern alongside implementation files
The query-resources endpoint supports a page_size query parameter to control how many documents are returned per page.
Query Parameter:
page_size(int, optional): Number of results per page (min: 1, max: 1000, default: 50)
Examples:
# Custom page size
GET /query/resources?v=1&type=project&page_size=20
# Combined with other filters
GET /query/resources?v=1&type=project&tags=active&date_field=updated_at&date_from=2025-01-01&page_size=100Interaction with post-query filters: cel_filter and access control checks are applied after OpenSearch returns results, so a page may return fewer than page_size results.
Implementation Details:
- Goa design:
design/types.go(Sortabletype),design/query-svc.go(HTTP param) - Converter:
cmd/service/converters.go(payloadToCriteria()passesp.PageSizeto criteria) - Domain model:
internal/domain/model/search_criteria.go(PageSizefield) - OpenSearch pagination:
internal/infrastructure/opensearch/client.go(page token generated whenlen(hits) == pageSize) - Constants:
pkg/constants/query.go(DefaultPageSize,MaxPageSize)
The query service supports filtering resources by date ranges on fields within the data object.
Query Parameters:
date_field(string, optional): Date field to filter on (automatically prefixed with"data.")date_from(string, optional): Start date (inclusive, gte operator)date_to(string, optional): End date (inclusive, lte operator)
Supported Date Formats:
- ISO 8601 datetime:
2025-01-10T15:30:00Z(time used as provided) - Date-only:
2025-01-10(converted to start/end of day UTC)date_from→2025-01-10T00:00:00Z(start of day)date_to→2025-01-10T23:59:59Z(end of day)
Examples:
# Date range with date-only format
GET /query/resources?v=1&date_field=updated_at&date_from=2025-01-10&date_to=2025-01-28
# Date range with ISO 8601 format
GET /query/resources?v=1&date_field=created_at&date_from=2025-01-10T15:30:00Z&date_to=2025-01-28T18:45:00Z
# Open-ended range (only start date)
GET /query/resources?v=1&date_field=created_at&date_from=2025-01-01
# Combined with other filters
GET /query/resources?v=1&type=project&tags=active&date_field=updated_at&date_from=2025-01-01&date_to=2025-03-31Implementation Details:
- Date parsing logic:
cmd/service/converters.go(parseDateFilter()function) - Domain model:
internal/domain/model/search_criteria.go(DateField, DateFrom, DateTo) - OpenSearch query:
internal/infrastructure/opensearch/template.go(range query with gte/lte) - API design:
design/query-svc.go(Goa design specification) - Test coverage:
cmd/service/converters_test.go(17 comprehensive test cases)
The service supports Common Expression Language (CEL) filtering for post-query resource filtering.
CEL filtering allows API consumers to filter resources on arbitrary data fields using a safe, non-Turing complete expression language. The filter is applied after the OpenSearch query but before access control checks.
Location: internal/infrastructure/filter/cel_filter.go
Key Components:
- ResourceFilter Interface (
internal/domain/port/filter.go): Domain interface for filtering - CELFilter Implementation: Uses
google/cel-golibrary for expression evaluation - Expression Caching: LRU cache with TTL for compiled CEL programs
- Security Features: Max expression length (1000 chars), evaluation timeout (100ms per resource)
Integration Point: internal/service/resource_search.go (lines 84-102)
- CEL filter applied after OpenSearch query
- Filters resources before access control checks
- Reduces number of access control checks needed
Available Variables in CEL Expressions:
data(map): Resource data objectresource_type(string): Resource typeid(string): Resource ID
Note: type is a reserved word in CEL, so we use resource_type instead.
Example Usage:
GET /query/resources?type=project&cel_filter=data.slug == "tlf"Common CEL Operations:
- Equality:
data.status == "active" - Comparison:
data.priority > 5 - Boolean logic:
data.status == "active" && data.priority > 5 - String operations:
data.name.contains("LF") - List membership:
data.category in ["security", "networking"] - Field existence:
has(data.archived)
Performance Considerations:
- Compiled CEL programs are cached (100 max entries, 5-minute TTL)
- Each resource evaluation has 100ms timeout
- Post-query filtering means pagination may return fewer results than page size
- For best performance, use specific OpenSearch criteria first, then CEL for refinement
Important Limitations:
CEL filters apply only to results from each OpenSearch page. If the target resource is not in the first page of OpenSearch results, it won't be found even if it matches the CEL filter. Always use specific primary search criteria (type, name, parent) to narrow OpenSearch results first.