Contributions are what make the open-source community such an amazing place to learn, inspire, and create.
Thank you for your interest in contributing to the LFX V2 Survey Service! This document provides guidelines and instructions for contributing to the project.
- Code of Conduct
- Getting Started
- Development Setup
- Local Infrastructure (NATS + Heimdall)
- Getting Dev Credentials
- License Headers
- Code Style
- Architecture Guidelines
- Adding New Endpoints
- Commit Messages
- Pull Request Process
- Testing
By participating in this project, you agree to abide by the Linux Foundation Code of Conduct.
- Fork the repository
- Clone your fork locally
- Create a new branch for your feature or bug fix
- Make your changes
- Push your changes to your fork
- Submit a pull request
Please refer to the README.md for detailed setup instructions, including all environment variables and how to run the service locally.
# Install dependencies (Go 1.25+ required)
make deps
# Generate API code from Goa design
make apigen
# Build the service
make build
# Set up your local environment (fill in ITX credentials — see below)
cp .env.example .env
# Run the service
source .env && make runThe .env.example file has all variables pre-configured for local development with sensible defaults — JWT auth, NATS ID mapping, and event processing are all disabled out of the box. The only values you need to fill in are ITX_CLIENT_ID and ITX_CLIENT_PRIVATE_KEY.
Run make help to see all available targets.
The service depends on NATS and Heimdall. Install the lfx-platform Helm chart to run both locally:
kubectl create namespace lfx
# Latest version (may include breaking changes):
helm install -n lfx lfx-platform \
oci://ghcr.io/linuxfoundation/lfx-v2-helm/chart/lfx-platform
# Pinned version (recommended for reproducible local setup):
helm install -n lfx lfx-platform \
oci://ghcr.io/linuxfoundation/lfx-v2-helm/chart/lfx-platform \
--version <version>For available versions, see the lfx-v2-helm releases.
This provides NATS, Heimdall, Traefik, OpenFGA, and other platform services. The default NATS_URL in .env.example connects to this chart's NATS instance automatically.
If you want to skip the cluster entirely, the defaults in .env.example already disable NATS and Heimdall — just fill in the ITX credentials and run.
The service requires ITX OAuth2 credentials (ITX_CLIENT_ID and ITX_CLIENT_PRIVATE_KEY). Find them in:
1Password → Linux Foundation org → LFX V2 vault → LFX Platform Chart Values Secrets - Local Development (secure note)
- The private key is an RSA key in PEM format
- Store the key locally at
tmp/local.private.key(gitignored) and reference it with$(cat tmp/local.private.key)
For local development, you can bypass NATS and JWT auth entirely using the flags shown in the Quick Start above. The only credential you truly need to make proxy calls to the ITX dev environment is ITX_CLIENT_ID and ITX_CLIENT_PRIVATE_KEY.
IMPORTANT: All source code files must include the appropriate license header. This is enforced by our CI/CD pipeline.
The license header must appear at the top of every source file and must contain:
Copyright The Linux Foundation and each contributor to LFX.
SPDX-License-Identifier: MIT
// Copyright The Linux Foundation and each contributor to LFX.
// SPDX-License-Identifier: MIT
package yourpackage# Copyright The Linux Foundation and each contributor to LFX.
# SPDX-License-Identifier: MIT# Copyright The Linux Foundation and each contributor to LFX.
# SPDX-License-Identifier: MIT- CI Pipeline: GitHub Actions verifies all files have proper headers on every pull request (excludes
gen/*andcmd/survey-api/kodata/*)
- Follow Effective Go
- Use
gofmtfor formatting — runmake fmtbefore committing - All exported functions and types must have doc comments
- Domain errors use the
DomainErrorpattern (seeinternal/domain/errors.go) - Use structured logging (
slog) — neverfmt.Printlnorlog.Printf
The project uses golangci-lint. Run linting before committing:
# Run linter
make lint
# Check formatting + lint without modifying files
make checkrevive.toml configures the standalone revive linter used by MegaLinter in CI. make lint runs golangci-lint with its built-in defaults (no .golangci.yml in this repo).
This service uses clean architecture with clear layer boundaries. Understand them before making changes:
api/survey/v1/design/— Goa DSL only; no business logicinternal/domain/— interfaces and domain models; no external dependenciesinternal/service/— business logic; depends only on domain interfacesinternal/infrastructure/— external integrations (ITX proxy, NATS, auth)cmd/survey-api/— wiring, converters, and Goa interface implementation
Dependencies flow inward. Infrastructure depends on domain, not the other way around.
When something doesn't work, fix the root cause. Disabling auth, bypassing linting with //nolint without explanation, or adding build hacks are not fixes. TODO: TEMPORARY bypasses have a tendency to reach production.
Changes that affect the entire application — middleware, NATS consumer configuration, new infrastructure integrations — are architectural decisions. They need standalone PRs with focused review, not bundled inside feature work.
This service is a proxy. Every endpoint here corresponds to an endpoint in the upstream ITX SurveyMonkey service — the ITX endpoint must exist before you proxy it here. If the capability you need doesn't exist in ITX yet, start there: github.com/linuxfoundation-it/itx-service-survey-monkey.
Once the ITX endpoint is available, follow these steps in order to proxy it:
Edit api/survey/v1/design/survey.go and add a new Method(...) block inside the Service("survey", ...) declaration. Add any new request/response types to api/survey/v1/design/types.go.
Method("my_new_endpoint", func() {
Description("Description of what this proxies to in ITX")
Payload(func() {
BearerTokenAttribute()
Attribute("survey_uid", String, "Survey UID")
Required("survey_uid")
})
Result(MyNewResult)
HTTP(func() {
POST("/surveys/{survey_uid}/my-action")
Response(StatusOK)
})
})make apigenThis overwrites gen/ with updated handlers, types, and OpenAPI spec. Do not manually edit files in gen/.
Add the new method signature to the appropriate interface in internal/domain/proxy.go (SurveyClient or SurveyResponseClient).
Add the corresponding method to internal/infrastructure/proxy/client.go. This is where the HTTP call to ITX is made, including the path translation (e.g., /surveys/{id} → /v2/surveys/{id}/schedule). Add any new ITX request/response models to pkg/models/itx/.
Implement the business logic in internal/service/survey_service.go. This is where authentication parsing, ID mapping (v2 UUID → v1 Salesforce ID), field transformation, and error mapping happen. See the existing methods for the standard pattern.
Add the method to cmd/survey-api/api.go, which implements the Goa-generated interface. This should be a thin delegating call to the service layer.
Write unit tests for the service method using mock implementations of the domain interfaces.
- ITX upstream endpoint exists in itx-service-survey-monkey
- Goa DSL updated and
make apigenrun — generated files committed - Domain proxy interface updated
- ITX proxy method implemented with correct path mapping
- Service method implemented with ID mapping and error handling
- API handler added
- Unit tests written
- License headers on all new files
-
make checkandmake testpass
Follow the conventional commit format:
type(scope): subject
body
footer
feat: New featurefix: Bug fixdocs: Documentation changesstyle: Code style changes (formatting, etc.)refactor: Code refactoringtest: Adding or updating testschore: Maintenance tasks
feat(surveys): add bulk resend endpoint
Implements POST /surveys/{survey_uid}/bulk_resend to proxy the
ITX bulk email resend call.
Closes #42
All commits must be signed off per the Developer Certificate of Origin:
git commit --signoffThis adds a Signed-off-by line to your commit message.
Use the format <type>/<ticket-or-description>, for example:
feat/LFXV2-123-add-bulk-resendfix/LFXV2-456-fix-id-mappingchore/update-dependencies
- Keep PRs focused on a single concern — a feature PR should contain only the feature
- Architectural decisions require their own PR — changes to middleware, infrastructure wiring, or NATS configuration need standalone discussion and approval
- Never mix security changes with feature work — auth middleware modifications must be reviewed independently
- Update Documentation: Update README or
docs/for any new features or configuration changes - Add Tests: Include unit tests for new functionality
- Pass All Checks: Ensure
make checkandmake testpass locally - License Headers: Verify all new files have the correct license header
- API Generation: If you modified Goa design files, run
make apigenand commit the regenerated files - Clear Description: Provide a clear description of changes and link any related issues or Jira tickets
Use the same conventional commit format for PR titles:
feat(surveys): add bulk resend endpoint
# Run all tests
make test- All new features must include unit tests
- Tests must pass with
-raceflag (already enforced bymake test) - Mock external dependencies using the domain interfaces — tests should not require a live ITX or NATS connection