Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
d919c72
docs: add Heights API feature specification
0mega Jan 9, 2026
d5609de
docs: add implementation plan and research for Heights API
0mega Jan 9, 2026
38071ad
docs: add data model and API contracts for Heights API
0mega Jan 9, 2026
4e7c362
docs: add quickstart guide and implementation tasks
0mega Jan 9, 2026
2f60ab0
refactor: make TidesCallback generic to support multiple result types
0mega Jan 9, 2026
fee7aec
refactor: move Repository and Gateway to package root
0mega Jan 9, 2026
9a7635d
feat: add getTideHeights API for fetching tide height predictions
0mega Jan 9, 2026
dd70902
feat: add getTides API for flexible multi-type tide data requests
0mega Jan 9, 2026
eee6c54
feat: add getTideHeights and getTides public API methods
0mega Jan 9, 2026
e4ff00f
docs: update README with Heights and Tides API documentation
0mega Jan 9, 2026
f2d1b07
docs: mark implementation tasks complete in tasks.md
0mega Jan 9, 2026
fec58db
test: add comprehensive tests for Heights and Tides API
0mega Jan 10, 2026
346d760
docs: update CHANGELOG and complete Phase 5 tasks
0mega Jan 10, 2026
433d96e
fix: make Height.date non-nullable for consistency with Extreme.date
0mega Jan 15, 2026
53fba55
refactor: extract DTO-to-domain mapping into extension functions
0mega Jan 15, 2026
e590484
refactor: extract enqueueMapped() to reduce callback boilerplate
0mega Jan 15, 2026
d0c215f
docs: update CHANGELOG.md for v2.0.0 breaking changes
0mega Jan 15, 2026
99e1d76
test: remove duplicate and trivial tests
0mega Jan 15, 2026
8ecc2b9
chore: bump version to 2.0.0
0mega Jan 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 35 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,53 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## UNRELEASED
## [2.0.0] - 2026-01-15

### ⚠️ BREAKING CHANGES

- **`TidesCallback` is now generic**: The callback interface has been refactored to `TidesCallback<T>` to support multiple response types.

**Migration for Java users:**
```java
// Before
new TidesCallback() {
@Override public void result(TideExtremes tides) { }
@Override public void error(Error error) { }
}

// After
new TidesCallback<TideExtremes>() {
@Override public void result(TideExtremes data) { }
@Override public void error(Error error) { }
}
```

- **`Height.date` is now non-nullable**: Changed from `Date?` to `Date` for consistency with `Extreme.date`.

### Added

- SpekKit
- Project constitution
- Github copilot instructions for commit summaries
- `getTideHeights()` - Fetch predicted tide heights for a location
- `getTides()` - Flexible method to fetch any combination of tide data types
- `TideDataType` enum for specifying data types (HEIGHTS, EXTREMES)
- `Tides` model for combined tide data responses
- `TideHeights` and `Height` models for height data
- Comprehensive test coverage for all new functionality

### Changed

- Refactored `WorldTidesRepository` and `WorldTidesGateway` to package root for shared usage
- Internal: Added DTO mapping extension functions for cleaner code
- Internal: Extracted `enqueueMapped()` extension for callback boilerplate

### Internal

- Bump Kotlin version to 1.9.20
- Bump Gradle to 8.9
- Bump mockito to 5.13.0
- Bump to JDK_1_8
- Replace jcenter() with mavenCentral()
- Update github actions setup
- Added SpekKit and project constitution

## 1.0.0 - 2021-02-15

Expand Down
138 changes: 113 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,47 +29,135 @@ dependencies {

| API Request | API version | Supported | Planned |
| ----------- | ----------- | --------- | ------- |
| Extremes | v2 | Yes | Yes |
| Heights | v2 | No | Yes |
| Extremes | v2 | Yes | - |
| Heights | v2 | Yes | - |
| Stations | v2 | No | Yes |
| Datum | v2 | No | Yes |

## Usage

Following snippet demonstrates how to use world tides lib to fetch tides extremes from [worldtides.info](https://www.worldtides.info/apidocs).
### Get Tide Extremes

<details open>
<summary>Get Tide Extremes - Kotlin</summary>

```Kotlin
val worldTides = WorldTides.Builder().build(apiKey)
worldTides.getTideExtremes(date, days, lat, lon, object : TidesCallback {
override fun result(result: Result<TideExtremes>) {
result.onSuccess { tideExtremes ->
// use the tide extremes here as you wish
}
<summary>Kotlin</summary>

```kotlin
val worldTides = WorldTides.Builder().build(apiKey)
worldTides.getTideExtremes(date, days, lat, lon) { result ->
result.onSuccess { tideExtremes ->
tideExtremes.extremes.forEach { extreme ->
println("${extreme.type} at ${extreme.date}: ${extreme.height}m")
}
})
}
result.onFailure { error ->
println("Error: ${error.message}")
}
}
```

</details>

<details>
<summary>Get Tide Extremes - Java</summary>

```Java
WorldTides wt = (new WorldTides.Builder()).build(apiKey);
wt.getTideExtremes(date, 1, latitude, longitude, new TidesCallback() {
@Override
public void onResult(@NotNull TideExtremes tides) {
// Use the tide extremes
<summary>Java</summary>

```java
WorldTides wt = new WorldTides.Builder().build(apiKey);
wt.getTideExtremes(date, 7, lat, lon, new TidesCallback<TideExtremes>() {
@Override
public void result(TideExtremes tides) {
// Use the tide extremes
}

@Override
public void error(Error error) {
// Handle error
}
});
```

</details>

### Get Tide Heights

<details open>
<summary>Kotlin</summary>

```kotlin
val worldTides = WorldTides.Builder().build(apiKey)
worldTides.getTideHeights(date, days, lat, lon) { result ->
result.onSuccess { tideHeights ->
tideHeights.heights.forEach { height ->
println("Height at ${height.date}: ${height.height}m")
}
}
result.onFailure { error ->
println("Error: ${error.message}")
}
}
```

</details>

<details>
<summary>Java</summary>

```java
WorldTides wt = new WorldTides.Builder().build(apiKey);
wt.getTideHeights(date, 7, lat, lon, new TidesCallback<TideHeights>() {
@Override
public void result(TideHeights heights) {
// Use the tide heights
}

@Override
public void error(Error error) {
// Handle error
}
});
```

</details>

### Get Flexible Tide Data (Combined)

Fetch multiple data types in a single API call:

<details open>
<summary>Kotlin</summary>

```kotlin
val dataTypes = listOf(TideDataType.HEIGHTS, TideDataType.EXTREMES)
worldTides.getTides(date, days, lat, lon, dataTypes) { result ->
result.onSuccess { tides ->
tides.heights?.let { println("Heights: ${it.heights.size} points") }
tides.extremes?.let { println("Extremes: ${it.extremes.size} points") }
}
}
```

</details>

@Override
public void onError() {
// Report an error
<details>
<summary>Java</summary>

```java
List<TideDataType> dataTypes = Arrays.asList(TideDataType.HEIGHTS, TideDataType.EXTREMES);
wt.getTides(date, 7, lat, lon, dataTypes, new TidesCallback<Tides>() {
@Override
public void result(Tides tides) {
if (tides.getHeights() != null) {
// Use heights data
}
if (tides.getExtremes() != null) {
// Use extremes data
}
});
}

@Override
public void error(Error error) {
// Handle error
}
});
```

</details>
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
GROUP=com.oleksandrkruk
VERSION=1.0.0
VERSION=2.0.0

POM_NAME=WorldTides API Client
POM_DESCRIPTION=A client for consuming https://www.worldtides.info/apidocs API
Expand Down
34 changes: 34 additions & 0 deletions specs/001-support-heights-api/checklists/requirements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Specification Quality Checklist: Support Heights API

**Purpose**: Validate specification completeness and quality before proceeding to planning
**Created**: 2026-01-09
**Feature**: [Link to spec.md](../spec.md)

## Content Quality

- [x] No implementation details (languages, frameworks, APIs)
- [x] Focused on user value and business needs
- [x] Written for non-technical stakeholders
- [x] All mandatory sections completed

## Requirement Completeness

- [x] No [NEEDS CLARIFICATION] markers remain
- [x] Requirements are testable and unambiguous
- [x] Success criteria are measurable
- [x] Success criteria are technology-agnostic (no implementation details)
- [x] All acceptance scenarios are defined
- [x] Edge cases are identified
- [x] Scope is clearly bounded
- [x] Dependencies and assumptions identified

## Feature Readiness

- [x] All functional requirements have clear acceptance criteria
- [x] User scenarios cover primary flows
- [x] Feature meets measurable outcomes defined in Success Criteria
- [x] No implementation details leak into specification

## Notes

- Spec is ready for Plan/Clarify.
12 changes: 12 additions & 0 deletions specs/001-support-heights-api/contracts/TidesCallback.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.oleksandrkruk.worldtides

/**
* Generic callback interface for WorldTides API responses.
* Supports TideExtremes, TideHeights, TideCombined, and future types.
*
* @param T The result type.
*/
interface TidesCallback<T> {
fun result(data: T)
fun error(error: Error)
}
90 changes: 90 additions & 0 deletions specs/001-support-heights-api/contracts/WorldTides.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.oleksandrkruk.worldtides

import com.oleksandrkruk.worldtides.heights.models.TideHeights
import com.oleksandrkruk.worldtides.models.Tides
import com.oleksandrkruk.worldtides.models.TideDataType
import java.util.Date

// This contract defines the changes to the public API
class WorldTides {

// ... existing properties and constructor ...

/**
* Returns the predicted tide heights between [date] and number of [days] in future.
*
* @param date starting date
* @param days number of days (duration)
* @param lat latitude
* @param lon longitude
* @param callback result handler (Kotlin lambda)
*/
fun getTideHeights(
date: Date,
days: Int,
lat: String,
lon: String,
callback: (Result<TideHeights>) -> Unit
) {
// Implementation
}

/**
* Java-Compatible overload for tide heights.
*/
fun getTideHeights(
date: Date,
days: Int,
lat: String,
lon: String,
callback: TidesCallback<TideHeights>
) {
// Implementation
}

/**
* Returns tide data based on the specified [dataTypes].
* Supports stacking multiple data types in a single API call.
*
* @param date starting date
* @param days number of days (duration)
* @param lat latitude
* @param lon longitude
* @param dataTypes list of data types to request (e.g., HEIGHTS, EXTREMES)
* @param callback result handler (Kotlin lambda)
*/
fun getTides(
date: Date,
days: Int,
lat: String,
lon: String,
dataTypes: List<TideDataType>,
callback: (Result<Tides>) -> Unit
) {
// Implementation
}

/**
* Java-Compatible overload for flexible tide data.
*/
fun getTides(
date: Date,
days: Int,
lat: String,
lon: String,
dataTypes: List<TideDataType>,
callback: TidesCallback<Tides>
) {
// Implementation
}
}

/**
* Enum representing the types of tide data that can be requested.
* Future versions will add STATIONS, DATUMS, etc.
*/
enum class TideDataType {
HEIGHTS,
EXTREMES
// Future: STATIONS, DATUMS
}
Loading