This Spring Boot web application converts Eventor IOF-XML files to JSON format, which is used in OriGo apps. The application provides REST API endpoints to fetch and convert event data from various Eventor federations.
The OriGo EventorApi acts as a bridge between Eventor's IOF-XML format and OriGo applications, providing:
- Event information (details, classes, documents)
- Entry lists with intelligent merging from multiple sources
- Start lists with timing information
- Result lists with split times
- Fee structures with class associations
- Organization and participant data
- Multi-source data merging: Intelligently combines entry lists, start lists, and result lists
- Event management: Supports single race events, multi-race events, and relay events
- Fee handling: Manages entry fees with class-specific pricing
- Database persistence: Caches event data for improved performance
- RESTful API: Clean JSON responses for easy integration
- Multi-federation support: Works with different Eventor federations (Norway, Sweden, etc.)
- Language: Kotlin (primary) with Java (JAXB-generated classes)
- Framework: Spring Boot 4.0.0
- Build Tool: Maven
- Java Version: Java 21
- Database: PostgreSQL with JDBC (migrated from JPA/Hibernate)
- API Documentation: SpringDoc OpenAPI (Swagger)
- XML Processing: JAXB for IOF-XML schema
- Authentication: JWT tokens
- Java 21 or higher
- Maven 3.6.0 or higher
- PostgreSQL database
src/
├── main/
│ ├── kotlin/no/stunor/origo/eventorapi/
│ │ ├── api/ # External API clients (Eventor API)
│ │ ├── controller/ # REST controllers
│ │ ├── data/ # JDBC repositories
│ │ ├── exception/ # Custom exceptions
│ │ ├── model/ # Data models and domain objects
│ │ │ ├── event/ # Event-related models
│ │ │ ├── organisation/ # Organization models
│ │ │ └── person/ # Person and membership models
│ │ ├── persistence/ # Custom Hibernate types
│ │ │ └── hibernate/ # Array and enum type handlers
│ │ ├── security/ # JWT and authentication
│ │ └── services/ # Business logic
│ │ └── converter/ # IOF-XML to JSON converters
│ └── resources/
│ ├── IOF.xsd # IOF-XML schema (JAXB source)
│ ├── application.yml # Production configuration
│ └── application-local.yml # Local development config (not in git)
└── test/
└── kotlin/ # Unit and integration tests
- API Request → REST Controller receives request
- Service Layer → EventService orchestrates data fetching
- Eventor API → EventorService calls external Eventor API
- Converters → Transform IOF-XML to domain models
- Repository → Persist/retrieve data from PostgreSQL
- Response → Return JSON to client
-
EventService: Main business logic for event data management
- Fetches events from Eventor API
- Merges entry lists from multiple sources (entry/start/result)
- Handles fee and class associations
- Intelligent deduplication and status tracking
-
Repositories: JDBC-based data access layer
- EventRepository: Event CRUD operations
- EventClassRepository: Event class management
- FeeRepository: Fee structure management
- PersonRepository, OrganisationRepository, etc.
-
Converters: Transform Eventor IOF-XML to JSON
- EventConverter: Event details
- EntryListConverter, StartListConverter, ResultListConverter
- FeeConverter: Entry fee structures
The application uses PostgreSQL with custom enum types:
event_type,event_classification,event_statusclass_type,class_gender- Array types for disciplines, punching units, timestamps
Key tables:
event: Event informationclass: Event classesfee: Entry fees with many-to-many to classesperson,organisation: Participant datauser: Application users with JWT authentication
The application uses Spring profiles for configuration:
application.yml- Production configuration using environment variablesapplication-local.yml- Local development configuration (not in version control)application-local.yml.example- Template for local development configuration
-
Copy the example configuration file:
cp src/main/resources/application-staging.yml src/main/resources/application-staging.yml
-
Update
application-local.ymlwith your local values:- Set your local PostgreSQL password
- Set a JWT secret (minimum 32 characters)
-
The
application-local.ymlfile is ignored by git to prevent committing credentials
POSTGRES_DB- PostgreSQL database connection URLPOSTGRES_USER- Database usernamePOSTGRES_PASSWORD- Database passwordJWT_SECRET- Secret key for JWT token signing (minimum 32 characters)
- Use the
application-local.yml.exampletemplate for local development - The actual
application-local.ymlfile is ignored by git - Always use environment variables for production deployments
- Never commit credentials, API keys, or secrets to the repository
The project has been upgraded to Spring Boot 4.0.0 and migrated from JPA/Hibernate to native JDBC:
Database Layer Changes:
- Migrated from JPA entities to JDBC-based repositories using
JdbcTemplate - Implemented custom Hibernate types for PostgreSQL arrays and enums
- Added
EventClassRepositoryfor managing event classes - Improved query performance with direct SQL
Benefits:
- Better control over SQL queries and performance
- Simplified array and enum handling with PostgreSQL
- Reduced complexity and boilerplate code
- More predictable transaction behavior
Breaking Changes:
- Removed JPA annotations from models
- Repository interfaces now use custom JDBC implementations
- Some test annotations changed (e.g.,
@DataJpaTestno longer used)
The EventService now implements intelligent entry list merging:
Strategy:
- Fetch entry list (registered participants)
- Fetch start list as fallback (if entry list is empty)
- Fetch result list (actual participants with results)
- Merge data with result taking priority
- Mark registered but non-participating entries as "Deregistered"
Deduplication:
- Primary key: Person ID or Team name
- Fallback: Composite key (name + organization + class + race)
- Handles split times, punching units, and team members
The project generates Java classes from IOF.xsd:
- Generated classes are in
target/generated-sources/jaxb/ - Don't manually edit generated classes
- Run
mvn generate-sourcesafter schema changes
PostgreSQL enum types must match Kotlin enum values:
event_classification→EventClassificationEnumevent_status→EventStatusEnumevent_type→EventFormEnumclass_type→EventClassTypeEnumclass_gender→ClassGender
# Run all tests
mvn test
# Run specific test
mvn test -Dtest=EventClassRepositoryTest
# Run with debug output
mvn test -XIssue: Unresolved reference 'WebMvcTest'
- Some old test files use deprecated Spring Boot test annotations
- These are being gradually updated to
@SpringBootTest
# Clean and compile
mvn clean compile
# Generate JAXB classes from IOF.xsd
mvn generate-sources
# Full build with tests
mvn clean install# Run with local profile
mvn spring-boot:run
# Run with specific profile
mvn spring-boot:run -Dspring-boot.run.profiles=local
# Run tests
mvn testGET /api/eventor/{eventorId}/event/{eventId}- Get event details with classes and feesGET /api/eventor/{eventorId}/event/{eventId}/entries- Get entry list with merged dataGET /api/eventor/{eventorId}/event/{eventId}/starts- Get start listGET /api/eventor/{eventorId}/event/{eventId}/results- Get result list
GET /api/eventor/{eventorId}/person/{personId}/events- Get person's events
GET /api/eventor/{eventorId}/organisation/{organisationId}- Get organization details
POST /api/auth/login- Authenticate and receive JWT tokenPOST /api/users- Register new user
When running locally, access the interactive API documentation at:
- Swagger UI:
http://localhost:8080/rest/swagger-ui.html - OpenAPI JSON:
http://localhost:8080/rest/v3/api-docs
The project includes a Bruno collection in the bruno/ directory with pre-configured requests for all endpoints.
# Get event details
curl -X GET "http://localhost:8080/rest/api/eventor/NOR/event/12345"
# Get entry list
curl -X GET "http://localhost:8080/rest/api/eventor/NOR/event/12345/entries"
# Authenticate
curl -X POST "http://localhost:8080/rest/api/auth/login" \
-H "Content-Type: application/json" \
-d '{"username":"user","password":"password"}'This project follows Conventional Commits specification for commit messages and PR titles.
Kotlin Style:
- Use Kotlin idioms and conventions
- Prefer data classes for models
- Use nullable types (
?) appropriately - Use
whenexpressions overif-elsechains - Follow Spring Boot annotation conventions
Testing:
- Use JUnit 5 for tests
- Use MockK for Kotlin mocking
- Test class names should end with
Test - Use descriptive test names with backticks:
`test description` - Mock external dependencies (repositories, services)
Repository Pattern:
- Use
JdbcTemplatefor database operations - Implement upsert logic with
ON CONFLICT DO UPDATE - Create custom
RowMapperfor entity mapping - Handle nullable fields appropriately
All PR titles must follow the Conventional Commits format:
<type>: <description>
or with optional scope:
<type>(<scope>): <description>
Allowed types:
feat: A new featurefix: A bug fixdocs: Documentation only changesstyle: Changes that do not affect the meaning of the code (white-space, formatting, etc)refactor: A code change that neither fixes a bug nor adds a featureperf: A code change that improves performancetest: Adding missing tests or correcting existing testsbuild: Changes that affect the build system or external dependenciesci: Changes to CI configuration files and scriptschore: Other changes that don't modify src or test filesrevert: Reverts a previous commit
Examples:
feat: add support for relay eventsfix: correct time calculation in resultsdocs: update API documentationchore(deps): update spring boot to 4.0.0
- Fork the repository
- Create a new branch (
git checkout -b feature-branch) - Make your changes with appropriate tests
- Ensure the build passes:
mvn clean install - Commit your changes using conventional commit format
- Push to the branch (
git push origin feature-branch) - Create a new Pull Request with a title following the conventional commit format
This project uses Release Please to automate releases:
- Release Please creates PRs with titles like
chore(main): release X.Y.Z - Release PRs ending with SNAPSHOT are automatically merged once all required checks pass
- After merge, a new release is created and Docker images are published to GHCR
- Version numbers follow Semantic Versioning
- GitHub Copilot Instructions - Detailed development guidelines
- Contributing Guide - Contribution guidelines
- Security Policy - Security and vulnerability reporting
- Changelog - Version history and changes
- Eventor API Documentation - Official Eventor API docs
- IOF XML Schema - IOF data standard
- Spring Boot Documentation - Spring Boot reference
- Kotlin Documentation - Kotlin language reference
- Bruno - API testing client (collection included in
bruno/directory) - IntelliJ IDEA - Recommended IDE for Kotlin/Spring development
This project is licensed under the MIT License - see the LICENSE file for details.