{"id":34529037,"url":"https://github.com/linuxfoundation/lfx-v2-query-service","last_synced_at":"2026-02-22T03:10:54.280Z","repository":{"id":304005325,"uuid":"1016860203","full_name":"linuxfoundation/lfx-v2-query-service","owner":"linuxfoundation","description":"LFX v2 Platform Query Service","archived":false,"fork":false,"pushed_at":"2026-02-03T00:01:08.000Z","size":526,"stargazers_count":0,"open_issues_count":1,"forks_count":2,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-03T13:27:48.687Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/linuxfoundation.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-07-09T16:28:43.000Z","updated_at":"2026-01-26T21:42:41.000Z","dependencies_parsed_at":"2026-02-03T02:03:07.374Z","dependency_job_id":null,"html_url":"https://github.com/linuxfoundation/lfx-v2-query-service","commit_stats":null,"previous_names":["linuxfoundation/lfx-v2-query-service"],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/linuxfoundation/lfx-v2-query-service","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linuxfoundation%2Flfx-v2-query-service","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linuxfoundation%2Flfx-v2-query-service/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linuxfoundation%2Flfx-v2-query-service/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linuxfoundation%2Flfx-v2-query-service/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/linuxfoundation","download_url":"https://codeload.github.com/linuxfoundation/lfx-v2-query-service/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linuxfoundation%2Flfx-v2-query-service/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29704409,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-21T23:35:04.139Z","status":"online","status_checked_at":"2026-02-22T02:00:08.193Z","response_time":110,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-12-24T05:26:22.209Z","updated_at":"2026-02-22T03:10:54.271Z","avatar_url":"https://github.com/linuxfoundation.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# lfx-v2-query-service\n\nHTTP service for LFX API consumers to perform access-controlled queries for LFX resources, including typeahead and full-text search.\n\n## Architecture Overview\n\nThe implementation follows the clean architecture principles where:\n\n- **Domain Layer**: Contains business logic and interfaces\n- **Service Layer**: Orchestrates business operations\n- **Infrastructure Layer**: Implements external dependencies\n- **Presentation Layer**: Handles HTTP/API concerns (generated by Goa)\n\n## Directory Structure\n\n```\n├── .github/                        # Github files\n│   └── workflows/                  # Github Action workflow files\n├── charts/                         # Helm charts\n├── design/                         # GOA design specification files\n├── gen/                            # GOA generated code (HTTP server, client, OpenAPI)\n├── cmd/                            # Services (main packages)\n│   └── service/                    # Service implementation\n├── internal/                       # Internal service packages\n│   ├── domain/                     # Domain logic layer\n│   │   ├── model/                  # Domain models and entities\n│   │   └── port/                   # Domain interfaces/ports\n│   ├── service/                    # Business logic/use cases layer\n│   ├── infrastructure/             # Infrastructure layer\n│   └── middleware/                 # HTTP middleware components\n└── pkg/                            # Shared packages for internal and external services\n```\n\n## Key Components\n\n### Domain Layer (`internal/domain/`)\n\n- **ResourceService**: Contains business logic and validation\n\n#### Model (`internal/domain/model/`)\n\n- **Domain Models**: Core business entities and data structures\n- **Value Objects**: Immutable objects that represent domain concepts\n\n#### Ports (`internal/domain/port/`)\n\n- **ResourceSearcher Interface**: Defines the contract for resource search operations\n- **OrganizationSearcher Interface**: Defines the contract for organization search operations  \n- **AccessControlChecker Interface**: Defines the contract for access control operations\n- **Authenticator Interface**: Defines the contract for authentication operations\n\n### Service Layer (`internal/service/`)\n\n- **Business Logic**: Application-specific business rules and operations\n- **Service Orchestration**: Coordinates between domain models and infrastructure\n\n### Infrastructure Layer (`internal/infrastructure/`)\n\n#### Authentication Implementation\n\nThe authentication system provides JWT-based authentication with support for Heimdall tokens:\n\n- **JWT Authentication (`internal/infrastructure/auth/jwt.go`)**:\n  - Validates JWT tokens using JWKS (JSON Web Key Set)\n  - Extracts custom claims including principal and email\n  - Supports PS256 signature algorithm (default for Heimdall)\n  - Configurable JWKS URL and audience\n\n- **Mock Authentication (`internal/infrastructure/mock/auth.go`)**:\n  - Uses environment variable for mock principal\n  - Bypasses JWT validation for local development\n\n**Authentication Configuration:**\n\n- `AUTH_SOURCE`: Choose between \"mock\" or \"jwt\" (default: \"jwt\")\n- `JWKS_URL`: JSON Web Key Set endpoint URL\n- `JWT_AUDIENCE`: Intended audience for JWT tokens\n- `JWT_AUTH_DISABLED_MOCK_LOCAL_PRINCIPAL`: Mock principal for development\n\n#### OpenSearch Implementation\n\nThe OpenSearch implementation includes query templates, a searcher, and a client for interacting with the OpenSearch cluster.\n\n#### NATS Implementation\n\nThe NATS implementation consists of a client, access control logic, and request/response models for messaging and access control.\n\n#### Clearbit Implementation\n\nThe Clearbit implementation provides organization search capabilities using the Clearbit Company API. It includes a client for API communication, searcher for organization queries, and configuration management for API credentials and settings.\n\n## Dependency Injection\n\nDependency injection is performed in `cmd/main.go`, where the concrete implementations for resource search and access control are selected based on configuration and then injected into the service constructor.\n\n## Benefits of This Architecture\n\n1. **Testability**: Easy to swap implementations for testing\n2. **Flexibility**: Can easily switch between different search backends and access control systems\n3. **Maintainability**: Clear separation of concerns\n4. **Scalability**: Easy to add new search and access control implementations\n5. **Independence**: Layers don't depend on external frameworks\n\n## Docker\n\n### Building the Docker Image\n\n#### Example: Build the Docker image with Make\n\n```bash\nmake docker-build\n```\n\n### Running with Docker\n\n#### Basic Docker Run\n\n```bash\nmake docker-run\n```\n\n## Usage\n\n### Running Locally\n\n#### With Mock Implementation (Default for Development)\n\n```bash\n# Using mock implementations\nSEARCH_SOURCE=mock ACCESS_CONTROL_SOURCE=mock go run cmd/main.go\n\n# With custom port\nSEARCH_SOURCE=mock ACCESS_CONTROL_SOURCE=mock go run cmd/main.go -p 3000\n```\n\n#### With Production Services\n\n```bash\n# production-like setup\nSEARCH_SOURCE=opensearch \\\nORG_SEARCH_SOURCE=clearbit \\\nACCESS_CONTROL_SOURCE=nats \\\nOPENSEARCH_URL={{placeholder}} \\\nOPENSEARCH_INDEX=resources \\\nNATS_URL{{placeholder}} \\\nCLEARBIT_CREDENTIAL=your_clearbit_api_key \\\nCLEARBIT_BASE_URL=https://company.clearbit.com \\\nCLEARBIT_TIMEOUT=30s \\\nCLEARBIT_MAX_RETRIES=5 \\\nCLEARBIT_RETRY_DELAY=2s \\\ngo run cmd/main.go\n```\n\n### Available Environment Variables\n\n**Search Implementation:**\n\n- `SEARCH_SOURCE`: Choose between \"mock\" or \"opensearch\" (default: \"opensearch\")\n\n**Organization Search Implementation:**\n\n- `ORG_SEARCH_SOURCE`: Choose between \"mock\" or \"clearbit\" (default: \"clearbit\")\n\n**OpenSearch Configuration:**\n\n- `OPENSEARCH_URL`: OpenSearch URL (default: `http://localhost:9200`)\n- `OPENSEARCH_INDEX`: OpenSearch index name (default: \"resources\")\n\n**Access Control Implementation:**\n\n- `ACCESS_CONTROL_SOURCE`: Choose between \"mock\" or \"nats\" (default: \"nats\")\n\n**NATS Configuration:**\n\n- `NATS_URL`: NATS server URL (default: `nats://localhost:4222`)\n- `NATS_TIMEOUT`: Request timeout duration (default: \"10s\")\n- `NATS_MAX_RECONNECT`: Maximum reconnection attempts (default: \"3\")\n- `NATS_RECONNECT_WAIT`: Time between reconnection attempts (default: \"2s\")\n\n**Clearbit Configuration:**\n\n- `CLEARBIT_CREDENTIAL`: Clearbit API key (required for organization search)\n- `CLEARBIT_BASE_URL`: Clearbit API base URL (default: `https://company.clearbit.com`)\n- `CLEARBIT_TIMEOUT`: HTTP client timeout for API requests (default: \"10s\")\n- `CLEARBIT_MAX_RETRIES`: Maximum number of retry attempts for failed requests (default: \"3\")\n- `CLEARBIT_RETRY_DELAY`: Delay between retry attempts (default: \"1s\")\n\n**Authentication Configuration:**\n\n- `AUTH_SOURCE`: Choose between \"mock\" or \"jwt\"\n- `JWKS_URL`: JSON Web Key Set endpoint URL\n- `JWT_AUDIENCE`: Intended audience for JWT tokens\n- `JWT_AUTH_DISABLED_MOCK_LOCAL_PRINCIPAL`: Mock principal for development (required when AUTH_SOURCE=mock)\n\n**Server Configuration:**\n\n- `-p`: HTTP port (default: \"8080\")\n- `-bind`: Interface to bind on (default: \"*\")\n- `-d`: Enable debug logging\n\n### API Usage\n\nThe service exposes a RESTful API through the Goa framework with JWT authentication:\n\n#### Resource Search API\n\n```\nGET /query/resources?name=committee\u0026type=committee\u0026v=1\nAuthorization: Bearer \u003cjwt_token\u003e\n```\n\n**Parameters:**\n\n- `name`: Resource name or alias (supports typeahead search)\n- `type`: Resource type to filter by\n- `parent`: Parent resource for hierarchical queries\n\u003c\u003c\u003c\u003c\u003c\u003c\u003c HEAD\n- `tags`: Array of tags to filter by (OR logic - matches resources with any of these tags)\n- `tags_all`: Array of tags to filter by (AND logic - matches resources that have all of these tags)\n- `date_field`: Date field to filter on (within data object) - used with date_from and/or date_to\n- `date_from`: Start date (inclusive). Format: ISO 8601 datetime or date-only (YYYY-MM-DD). Date-only uses start of day UTC\n- `date_to`: End date (inclusive). Format: ISO 8601 datetime or date-only (YYYY-MM-DD). Date-only uses end of day UTC\n=======\n- `tags`: Array of tags to filter by (OR logic)\n- `tags_all`: Array of tags where all must match (AND logic)\n- `cel_filter`: CEL expression for advanced post-query filtering (see [CEL Filter](#cel-filter) section)\n\u003e\u003e\u003e\u003e\u003e\u003e\u003e 3e45fc4d33aba656a5abe1c3df0d3f2bd0fd6be7\n- `sort`: Sort order (name_asc, name_desc, updated_asc, updated_desc)\n- `page_token`: Pagination token\n- `v`: API version (required)\n\n**Response:**\n\n```json\n{\n  \"resources\": [\n    {\n      \"type\": \"committee\",\n      \"id\": \"123\",\n      \"data\": {\n        \"name\": \"Technical Advisory Committee\",\n        \"description\": \"Main technical governance body\",\n        \"status\": \"active\"\n      }\n    }\n  ],\n  \"page_token\": \"offset_50\",\n  \"cache_control\": \"public, max-age=300\"\n}\n```\n\n\u003c\u003c\u003c\u003c\u003c\u003c\u003c HEAD\n**Date Range Filtering Examples:**\n\nFilter resources updated between two dates (date-only format):\n\n```bash\nGET /query/resources?v=1\u0026date_field=updated_at\u0026date_from=2025-01-10\u0026date_to=2025-01-28\nAuthorization: Bearer \u003cjwt_token\u003e\n```\n\nFilter resources with precise datetime filtering (ISO 8601 format):\n\n```bash\nGET /query/resources?v=1\u0026date_field=created_at\u0026date_from=2025-01-10T15:30:00Z\u0026date_to=2025-01-28T18:45:00Z\nAuthorization: Bearer \u003cjwt_token\u003e\n```\n\nFilter resources created after a specific date (open-ended range):\n\n```bash\nGET /query/resources?v=1\u0026date_field=created_at\u0026date_from=2025-01-01\nAuthorization: Bearer \u003cjwt_token\u003e\n```\n\nCombine date filtering with other parameters:\n\n```bash\nGET /query/resources?v=1\u0026type=project\u0026tags=active\u0026date_field=updated_at\u0026date_from=2025-01-01\u0026date_to=2025-03-31\nAuthorization: Bearer \u003cjwt_token\u003e\n```\n\n**Date Format Notes:**\n\n- **ISO 8601 datetime format**: `2025-01-10T15:30:00Z` (time is used as provided)\n- **Date-only format**: `2025-01-10` (automatically converted to start/end of day UTC)\n  - For `date_from`: Converts to `2025-01-10T00:00:00Z` (start of day)\n  - For `date_to`: Converts to `2025-01-10T23:59:59Z` (end of day)\n- All dates are inclusive (uses `gte` and `lte` operators)\n- The `date_field` parameter is automatically prefixed with `\"data.\"` to scope to the resource's data object\n=======\n#### CEL Filter\n\nThe `cel_filter` query parameter enables advanced filtering of search results using Common Expression Language (CEL). CEL is a non-Turing complete expression language designed for safe, fast evaluation of expressions in performance-critical applications.\n\n**Why CEL Filter?**\n\nCEL filtering was added to provide flexible, dynamic filtering capabilities on arbitrary resource data fields without modifying the OpenSearch query structure. This allows API consumers to:\n\n- Filter on any field within the resource data\n- Combine multiple conditions with boolean logic\n- Perform complex comparisons beyond simple equality checks\n- Apply filters without requiring backend code changes\n\n**What is CEL?**\n\nCEL (Common Expression Language) is an open-source expression language developed by Google. It provides:\n\n- **Safety**: Non-Turing complete, no side effects, no infinite loops\n- **Performance**: Linear time evaluation with compilation and caching\n- **Portability**: Language-agnostic with implementations in multiple languages\n- **Security**: Execution timeouts and resource constraints\n\nLearn more: [CEL Specification](https://github.com/google/cel-spec) | [CEL-Go Documentation](https://github.com/google/cel-go)\n\n**How It Works**\n\nCEL filters are applied **after** the OpenSearch query executes but **before** access control checks. This means:\n\n1. OpenSearch returns initial results based on primary search criteria (`type`, `name`, `parent`, `tags`)\n2. CEL filter evaluates each resource and removes non-matching items\n3. Access control checks are performed only on filtered results (improved performance)\n4. Final results are returned to the client\n\n**Available Variables**\n\nCEL expressions have access to the following variables for each resource:\n\n- `data` (map): The resource's data object containing all custom fields\n- `resource_type` (string): The type of the resource (e.g., \"project\", \"committee\")\n- `id` (string): The unique identifier of the resource\n\n**Security Constraints**\n\n- **Maximum expression length**: 1000 characters\n- **Evaluation timeout**: 100ms per resource\n- **Expression caching**: Compiled programs cached with LRU and 5-minute TTL\n- **No external access**: Cannot make network calls or access filesystem\n\n**Usage Examples**\n\nFilter projects by slug:\n```\nGET /query/resources?type=project\u0026cel_filter=data.slug == \"tlf\"\u0026v=1\n```\n\nFilter by status and priority:\n```\nGET /query/resources?type=project\u0026cel_filter=data.status == \"active\" \u0026\u0026 data.priority \u003e 5\u0026v=1\n```\n\nFilter by resource type:\n```\nGET /query/resources?parent=org:123\u0026cel_filter=resource_type == \"committee\"\u0026v=1\n```\n\nComplex boolean logic:\n```\nGET /query/resources?type=project\u0026cel_filter=data.status == \"active\" || (data.priority \u003e 8 \u0026\u0026 data.category == \"security\")\u0026v=1\n```\n\nString operations:\n```\nGET /query/resources?type=project\u0026cel_filter=data.name.contains(\"LF\") \u0026\u0026 data.description.startsWith(\"Open\")\u0026v=1\n```\n\nCheck field existence:\n```\nGET /query/resources?type=project\u0026cel_filter=has(data.archived) \u0026\u0026 data.archived == false\u0026v=1\n```\n\nList membership:\n```\nGET /query/resources?type=project\u0026cel_filter=data.category in [\"security\", \"networking\", \"storage\"]\u0026v=1\n```\n\nNested field access:\n```\nGET /query/resources?type=project\u0026cel_filter=data.metadata.owner == \"admin\" \u0026\u0026 data.metadata.region == \"us-west\"\u0026v=1\n```\n\n**Supported Operators**\n\n- **Comparison**: `==`, `!=`, `\u003c`, `\u003c=`, `\u003e`, `\u003e=`\n- **Logical**: `\u0026\u0026` (AND), `||` (OR), `!` (NOT)\n- **Arithmetic**: `+`, `-`, `*`, `/`, `%`\n- **String**: `contains()`, `startsWith()`, `endsWith()`, `matches()` (regex)\n- **Membership**: `in`\n- **Field check**: `has()`\n\n**Important Limitations**\n\n⚠️ **Pagination Consideration**: CEL filters are applied to the results from each OpenSearch page. If you're looking for a specific resource that matches your CEL filter but it's not in the first page of OpenSearch results, it may not be found. For best results when using CEL filters, use more specific primary search parameters (`type`, `name`, `parent`, `tags`) to narrow down the OpenSearch results first.\n\n**Error Handling**\n\nInvalid CEL expressions return a 400 Bad Request with details:\n\n```json\n{\n  \"error\": \"filter expression failed: ERROR: \u003cinput\u003e:1:6: Syntax error: mismatched input 'invalid' expecting {'[', '{', '(', '.', '-', '!', 'true', 'false', 'null', NUM_FLOAT, NUM_INT, NUM_UINT, STRING, BYTES, IDENTIFIER}\"\n}\n```\n\u003e\u003e\u003e\u003e\u003e\u003e\u003e 3e45fc4d33aba656a5abe1c3df0d3f2bd0fd6be7\n\n#### Organization Search API\n\n**Query Organizations:**\n\n```\nGET /query/orgs?name=Linux Foundation\u0026domain=linuxfoundation.org\u0026v=1\nAuthorization: Bearer \u003cjwt_token\u003e\n```\n\n**Parameters:**\n\n- `name`: Organization name (optional)\n- `domain`: Organization domain or website URL (optional)\n- `v`: API version (required)\n\n**Response:**\n\n```json\n{\n  \"name\": \"Linux Foundation\",\n  \"domain\": \"linuxfoundation.org\",\n  \"industry\": \"Non-Profit\",\n  \"sector\": \"Technology\",\n  \"employees\": \"100-499\"\n}\n```\n\n**Organization Suggestions API:**\n\n```\nGET /query/orgs/suggest?query=linux\u0026v=1\nAuthorization: Bearer \u003cjwt_token\u003e\n```\n\n**Parameters:**\n\n- `query`: Search query for organization suggestions (required, minimum 1 character)\n- `v`: API version (required)\n\n**Response:**\n\n```json\n{\n  \"suggestions\": [\n    {\n      \"name\": \"Linux Foundation\",\n      \"domain\": \"linuxfoundation.org\",\n      \"logo\": \"https://example.com/logo.png\"\n    }\n  ]\n}\n```\n\n## Clearbit API Integration\n\nThe service integrates with Clearbit's Company API to provide enriched organization data for search operations. This integration allows the service to fetch detailed company information including industry classification, employee count, and domain information.\n\n### Clearbit API Setup\n\n#### 1. Obtain API Credentials\n\nTo use Clearbit integration, you need to obtain an API key from Clearbit:\n\n1. Sign up for a Clearbit account at [https://clearbit.com](https://clearbit.com)\n2. Navigate to your API settings to generate an API key\n3. Copy the API key for use in your environment configuration\n\n#### 2. Configure Environment Variables\n\nSet the required environment variables for Clearbit integration:\n\n```bash\n# Required: Clearbit API key\nexport CLEARBIT_CREDENTIAL=your_clearbit_api_key_here\n\n# Optional: Custom configuration (defaults shown)\nexport CLEARBIT_BASE_URL=https://company.clearbit.com\nexport CLEARBIT_TIMEOUT=30s\nexport CLEARBIT_MAX_RETRIES=3\nexport CLEARBIT_RETRY_DELAY=1s\n\n# Set organization search source to use Clearbit\nexport ORG_SEARCH_SOURCE=clearbit\n```\n\n#### 3. API Usage and Features\n\nThe Clearbit integration supports the following search operations:\n\n**Search by Company Name:**\n\n- Searches for companies using their registered business name\n- Falls back to domain-based search for additional data enrichment\n\n**Search by Domain:**\n\n- More accurate search method using company domain names\n- Provides comprehensive company information\n\n#### 4. Error Handling\n\nThe Clearbit integration includes robust error handling:\n\n- **404 Not Found**: Returns appropriate \"organization not found\" errors\n- **Rate Limiting**: Automatic retry with exponential backoff\n- **Network Issues**: Configurable retry attempts with delays\n- **API Errors**: Proper error propagation with context\n\n### Testing\n\nThe clean architecture makes testing straightforward:\n\n```go\n// Use mock implementations for unit tests\nsearcher := mock.NewMockResourceSearcher()\nsearcher.AddResource(testResource)\n\naccessChecker := mock.NewMockAccessControlChecker()\naccessChecker.SetAccessResult(testResult)\n\nservice := service.NewResourceService(searcher, accessChecker)\nresult, err := service.QueryResources(ctx, criteria)\n```\n\n### Extending the Architecture\n\nTo add a new search implementation:\n\n1. Create a new package in `internal/infrastructure/`\n2. Implement the `domain.ResourceSearcher` interface\n3. Add configuration options to `main.go`\n4. Update the dependency injection switch statement\n\nTo add a new access control implementation:\n\n1. Create a new package in `internal/infrastructure/`\n2. Implement the `domain.AccessControlChecker` interface\n3. Add configuration options to `main.go`\n4. Update the dependency injection switch statement\n\n## Development\n\n### Prerequisites\n\nThis project uses the [GOA Framework](https://goa.design/) for API generation. You'll need to install GOA before building the project.\n\n#### Installing GOA Framework\n\nFollow the [GOA installation guide](https://goa.design/docs/1-goa/quickstart/) to install GOA:\n\n```bash\ngo install goa.design/goa/v3/cmd/goa@latest\n```\n\nVerify the installation:\n\n```bash\ngoa version\n```\n\n### Building and Development\n\n#### 1. Generate Code\n\nThe project uses GOA to generate API code from the design specification. Run the following command to generate all necessary code:\n\n```bash\ngoa gen github.com/linuxfoundation/lfx-v2-query-service/design\n```\n\nThis command generates:\n\n- HTTP server and client code\n- OpenAPI specification\n- Service interfaces and types\n- Transport layer implementations\n\n#### 2. Initial Project Structure\n\n**Note**: The initial `cmd` structure was generated using GOA's example generator:\n\n```bash\ngoa example github.com/linuxfoundation/lfx-v2-query-service/design\n```\n\nThis command generated the basic server structure, which was then customized and adjusted to fit our project's clean architecture principles.\n\n#### 3. Development Workflow\n\n1. **Make design changes**: Edit files in the `design/` directory\n2. **Regenerate code**: Run `goa gen github.com/linuxfoundation/lfx-v2-query-service/design` after design changes\n3. **Build the project**:\n\n   ```bash\n   go build cmd\n   ```\n\n4. **Run with mock data** (for development):\n\n   ```bash\n   SEARCH_SOURCE=mock ACCESS_CONTROL_SOURCE=mock go run ./cmd\n   ```\n\n5. **Run tests**:\n\n   ```bash\n   make test\n\n   # or run go test to set custom flags\n   go test ./... -v\n   ```\n\n6. **Lint the code**\n\n   ```bash\n   # From the root of the directory, run megalinter (https://megalinter.io/latest/mega-linter-runner/) to ensure the code passes the linter checks. The CI/CD has a check that uses megalinter.\n   npx mega-linter-runner .\n   ```\n\n7. **Docker build + K8**\n\n    ```bash\n    # Build the dockerfile (from the root of the repo)\n    docker build -t lfx-v2-query-service:\u003crelease_number\u003e .\n\n    # Install the helm chart for the service into the lfx namespace (from the root of the repo)\n    helm install lfx-v2-query-service ./charts/lfx-v2-query-service/ -n lfx\n\n    # Once you have already installed the helm chart and need to just update it, use the following command (from the root of the repo):\n    helm upgrade lfx-v2-query-service ./charts/lfx-v2-query-service/ -n lfx\n    ```\n\n### Contributing\n\nTo contribute to this repository:\n\n1. Fork the repository\n2. Make your changes\n3. Submit a pull request\n\n## License\n\nCopyright The Linux Foundation and each contributor to LFX.\n\nThis project’s source code is licensed under the MIT License. A copy of the\nlicense is available in `LICENSE`.\n\nThis project’s documentation is licensed under the Creative Commons Attribution\n4.0 International License \\(CC-BY-4.0\\). A copy of the license is available in\n`LICENSE-docs`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flinuxfoundation%2Flfx-v2-query-service","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flinuxfoundation%2Flfx-v2-query-service","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flinuxfoundation%2Flfx-v2-query-service/lists"}