Skip to content

feat: Introduce public Polling API facade and internalize engine#9

Merged
bosankus merged 5 commits intomainfrom
feat/new_api_for_consumers
Sep 9, 2025
Merged

feat: Introduce public Polling API facade and internalize engine#9
bosankus merged 5 commits intomainfrom
feat/new_api_for_consumers

Conversation

@bosankus
Copy link
Owner

@bosankus bosankus commented Sep 6, 2025

This commit refactors the PollingEngine by introducing a public PollingApi interface and a Polling facade object as the primary entry point for consumers. The PollingEngine itself is now internal, encapsulating the implementation details.

Key Changes:

  • Public API Facade (Polling.kt, PollingApi.kt):
    • Introduced PollingApi interface defining the stable public contract for interacting with the polling functionality.
    • Created a Polling object that implements PollingApi and delegates calls to the internal PollingEngine. This is now the recommended way for apps to use the library.
    • PollingEngine.Handle is replaced by a public PollingSession data class, which is returned by Polling.startPolling and contains the session id.
  • PollingEngine.kt Internalization:
    • PollingEngine object and its State enum are now internal.
    • Public methods on PollingEngine (e.g., startPolling, pollUntil, cancel, pause, resume, updateBackoff, cancelAll, shutdown, compose) are now internal or fun (if they were already part of the internal API). Consumers should use the Polling facade.
    • Removed metrics recording calls (recordAttempt, recordResult, recordComplete) from PollingEngine as the Metrics interface was removed.
  • Configuration and Core (PollingConfig.kt, Core.kt):
    • Removed Logger and Metrics interfaces and their usage in PollingConfig and PollingConfigBuilder. Observability is now primarily through onAttempt, onResult, and onComplete hooks.
    • ErrorCodes object is now internal.
    • FetchStrategy, SuccessStrategy, RetryStrategy interfaces are now internal.
    • DefaultRetryPredicates object renamed to RetryPredicates and its retryOnNetworkServerTimeout predicate is renamed to networkOrServerOrTimeout.
    • The default throwableMapper in PollingConfig and PollingConfigBuilder now uses a stable public error code (-1) instead of the internal ErrorCodes.UNKNOWN_ERROR_CODE.
  • Build & Documentation:
    • Updated group ID in pollingengine/build.gradle.kts from io.github.bosankus to in.androidplay.
    • Updated README:
      • Changed artifact coordinates to in.androidplay:pollingengine.
      • Updated usage examples to use the new Polling facade and RetryPredicates.
      • Added a section on "Control APIs and Runtime Updates" demonstrating Polling.startPolling, pause, resume, updateBackoff, cancel.
      • Added examples for RetryPredicates.
      • Added a note about the API rename (PollingEngineApi -> PollingApi, use Polling facade).
      • Removed "Pluggable logging and metrics" from the feature list.
    • Added docs/PollingEngine.md (renamed from composeApp/src/androidMain/kotlin/in/androidplay/pollingengine/Platform.android.kt which was an empty file): This new Markdown file serves as a developer guide, explaining the public API, concepts, and usage.
    • Updated docs/ci-setup.md with the new timestamp.
    • Updated gradle/libs.versions.toml:
      • pollingengine artifact path updated to in.androidplay:pollingengine.
      • kotlinx-coroutines version updated to 1.10.2.
  • Sample App (App.kt):
    • Now uses the Polling facade instead of PollingEngine directly.
    • Uses Polling.run for one-shot polling instead of PollingEngine.startPolling with a job for simplified start/stop logic.
    • Removed PollingEngine.Handle and uses PollingSession (though direct handle usage is reduced due to Polling.run).
    • Pause/Resume functionality is now managed by a local isPaused state that controls the countdown timer, as Polling.run is synchronous. The actual engine pause/resume is not used in this simplified flow.
    • Stop functionality now cancels the pollingJob launched for Polling.run.
    • Added a retry strategy selector using RetryPredicates (Always, Never, Network/Server/Timeout).
    • Minor UI tweaks using AnimatedVisibility and RoundedCornerShape.
    • Round elapsed time values more consistently using kotlin.math.round.
  • Testing (PollingEngineCoreTests.kt, PollingEngineCancellationTest.kt):
    • Added PollingEngineCoreTests.kt with basic tests for success, non-retryable failure, max attempts, overall timeout, and compose behavior.
    • Updated PollingEngineCancellationTest.kt to use Polling.run.
  • Removed Files:
    • composeApp/src/commonMain/kotlin/in/androidplay/pollingengine/Greeting.kt
    • composeApp/src/commonMain/kotlin/in/androidplay/pollingengine/Platform.kt
    • composeApp/src/androidMain/kotlin/in/androidplay/pollingengine/Platform.android.kt (content moved to docs/PollingEngine.md)

Impact:

  • Provides a clearer and more stable public API for library consumers via the Polling facade.
  • Reduces the public API surface by internalizing engine details.
  • Simplifies observability by focusing on callback hooks rather than pluggable interfaces for logging/metrics.
  • The sample app demonstrates the new facade and provides a control to switch retry strategies.

This commit refactors the PollingEngine by introducing a public `PollingApi` interface and a `Polling` facade object as the primary entry point for consumers. The `PollingEngine` itself is now internal, encapsulating the implementation details.

**Key Changes:**

*   **Public API Facade (`Polling.kt`, `PollingApi.kt`):**
    *   Introduced `PollingApi` interface defining the stable public contract for interacting with the polling functionality.
    *   Created a `Polling` object that implements `PollingApi` and delegates calls to the internal `PollingEngine`. This is now the recommended way for apps to use the library.
    *   `PollingEngine.Handle` is replaced by a public `PollingSession` data class, which is returned by `Polling.startPolling` and contains the session `id`.
*   **`PollingEngine.kt` Internalization:**
    *   `PollingEngine` object and its `State` enum are now `internal`.
    *   Public methods on `PollingEngine` (e.g., `startPolling`, `pollUntil`, `cancel`, `pause`, `resume`, `updateBackoff`, `cancelAll`, `shutdown`, `compose`) are now `internal` or `fun` (if they were already part of the internal API). Consumers should use the `Polling` facade.
    *   Removed `metrics` recording calls (`recordAttempt`, `recordResult`, `recordComplete`) from `PollingEngine` as the `Metrics` interface was removed.
*   **Configuration and Core (`PollingConfig.kt`, `Core.kt`):**
    *   Removed `Logger` and `Metrics` interfaces and their usage in `PollingConfig` and `PollingConfigBuilder`. Observability is now primarily through `onAttempt`, `onResult`, and `onComplete` hooks.
    *   `ErrorCodes` object is now `internal`.
    *   `FetchStrategy`, `SuccessStrategy`, `RetryStrategy` interfaces are now `internal`.
    *   `DefaultRetryPredicates` object renamed to `RetryPredicates` and its `retryOnNetworkServerTimeout` predicate is renamed to `networkOrServerOrTimeout`.
    *   The default `throwableMapper` in `PollingConfig` and `PollingConfigBuilder` now uses a stable public error code (`-1`) instead of the internal `ErrorCodes.UNKNOWN_ERROR_CODE`.
*   **Build & Documentation:**
    *   Updated `group` ID in `pollingengine/build.gradle.kts` from `io.github.bosankus` to `in.androidplay`.
    *   Updated README:
        *   Changed artifact coordinates to `in.androidplay:pollingengine`.
        *   Updated usage examples to use the new `Polling` facade and `RetryPredicates`.
        *   Added a section on "Control APIs and Runtime Updates" demonstrating `Polling.startPolling`, `pause`, `resume`, `updateBackoff`, `cancel`.
        *   Added examples for `RetryPredicates`.
        *   Added a note about the API rename (`PollingEngineApi` -> `PollingApi`, use `Polling` facade).
        *   Removed "Pluggable logging and metrics" from the feature list.
    *   Added `docs/PollingEngine.md` (renamed from `composeApp/src/androidMain/kotlin/in/androidplay/pollingengine/Platform.android.kt` which was an empty file): This new Markdown file serves as a developer guide, explaining the public API, concepts, and usage.
    *   Updated `docs/ci-setup.md` with the new timestamp.
    *   Updated `gradle/libs.versions.toml`:
        *   `pollingengine` artifact path updated to `in.androidplay:pollingengine`.
        *   `kotlinx-coroutines` version updated to `1.10.2`.
*   **Sample App (`App.kt`):**
    *   Now uses the `Polling` facade instead of `PollingEngine` directly.
    *   Uses `Polling.run` for one-shot polling instead of `PollingEngine.startPolling` with a job for simplified start/stop logic.
    *   Removed `PollingEngine.Handle` and uses `PollingSession` (though direct handle usage is reduced due to `Polling.run`).
    *   Pause/Resume functionality is now managed by a local `isPaused` state that controls the countdown timer, as `Polling.run` is synchronous. The actual engine pause/resume is not used in this simplified flow.
    *   Stop functionality now cancels the `pollingJob` launched for `Polling.run`.
    *   Added a retry strategy selector using `RetryPredicates` (`Always`, `Never`, `Network/Server/Timeout`).
    *   Minor UI tweaks using `AnimatedVisibility` and `RoundedCornerShape`.
    *   Round elapsed time values more consistently using `kotlin.math.round`.
*   **Testing (`PollingEngineCoreTests.kt`, `PollingEngineCancellationTest.kt`):**
    *   Added `PollingEngineCoreTests.kt` with basic tests for success, non-retryable failure, max attempts, overall timeout, and `compose` behavior.
    *   Updated `PollingEngineCancellationTest.kt` to use `Polling.run`.
*   **Removed Files:**
    *   `composeApp/src/commonMain/kotlin/in/androidplay/pollingengine/Greeting.kt`
    *   `composeApp/src/commonMain/kotlin/in/androidplay/pollingengine/Platform.kt`
    *   `composeApp/src/androidMain/kotlin/in/androidplay/pollingengine/Platform.android.kt` (content moved to `docs/PollingEngine.md`)

**Impact:**

*   Provides a clearer and more stable public API for library consumers via the `Polling` facade.
*   Reduces the public API surface by internalizing engine details.
*   Simplifies observability by focusing on callback hooks rather than pluggable interfaces for logging/metrics.
*   The sample app demonstrates the new facade and provides a control to switch retry strategies.
@bosankus bosankus self-assigned this Sep 6, 2025
This commit introduces a major refactoring of the `PollingEngine` and its sample application. The engine's `startPolling` method now returns a `Flow<PollingOutcome<T>>` instead of taking a callback, and the sample app is restructured around a `PollingViewModel`.

**Key Changes:**

*   **`PollingEngine` (`PollingEngine.kt`, `Polling.kt`, `PollingAPI.kt`):**
    *   `startPolling` now returns `Flow<PollingOutcome<T>>`.
        *   The Flow emits the final `PollingOutcome` and then completes.
        *   Internal management of active polls uses `channelFlow` and `awaitClose` for proper cancellation.
    *   A new overload for `startPolling` that accepts a `PollingConfigBuilder<T>.() -> Unit` lambda is added for convenience.
    *   The `Handle` class is removed as Flow subscription management replaces its role.
    *   Minor adjustment: The pause check (`control.state.map { it == State.Running }.first { it }`) is moved after the `onAttempt` callback and before `delay()`, ensuring `onAttempt` is called even if immediately paused.

*   **Sample App (`App.kt`, `PollingViewModel.kt`, `Theme.kt`):**
    *   **ViewModel Architecture:**
        *   Introduced `PollingViewModel.kt` to manage UI state (`PollingUiState`), handle user intents (`PollingIntent`), and interact with the `PollingEngine`.
        *   `App.kt` now observes `StateFlow` from the ViewModel and dispatches intents.
    *   **State Management:**
        *   `PollingUiState` holds all UI-related data (running/paused state, logs, form inputs).
        *   Logs are now `List<LogItem>` with unique IDs for better `LazyColumn` performance.
        *   Uses `kotlinx.atomicfu.atomic` for unique log item IDs.
    *   **UI Enhancements (`App.kt`):**
        *   The main screen is now a `LazyColumn` for better scroll performance and structure.
        *   `ControlPanel.kt` (extracted from `App.kt`): A new composable for start/pause/resume/stop buttons and countdown timer.
        *   Properties section (`PropertiesCard` in previous commits, now inline in `App.kt` under "Basic Setup"):
            *   Layout improved with section headers (`SectionHeader`) and dividers (`SectionDivider`).
            *   `LabeledField` now supports `placeholder`, `suffix`, `supportingText`, `isError`, and `KeyboardType`.
            *   Retry strategy selection uses `Button` with visual distinction for the selected item.
        *   The "Apply Backoff at Runtime" button is moved inside the properties panel.
    *   **Theming (`Theme.kt`):**
        *   Extracted `MaterialTheme` setup into `PollingEngineTheme` in `Theme.kt`.
    *   **Dependencies (`composeApp/build.gradle.kts`, `gradle/libs.versions.toml`):**
        *   Added `kotlinx-atomicfu` dependency for `PollingViewModel`.

*   **Documentation (`README.md`, `docs/`):**
    *   Removed `docs/ci-setup.md` and `docs/PollingEngine.md` as they were marked ARCHIVED or superseded.
    *   `README.md`: Updated links to point to new `docs/pollingengine.md` and `docs/DeveloperGuide.md`.

*   **Build & Configuration:**
    *   `pollingengine/publishing.gradle.kts`: Minor formatting and null-check adjustments.
    *   Minor code style cleanups across various files (e.g., trailing commas, import organization).

*   **New Builder DSL (`pollingengine/polling/builder/PollingConfigBuilder.kt`):**
    *   Introduced `PollingConfigBuilder` with `@PollingBuilderMarker` DSL for creating `PollingConfig` instances more fluently.
    *   The `Polling.startPolling` facade now has an overload accepting this builder.

**Impact:**

*   The `PollingEngine` API is now more idiomatic for Kotlin Coroutines users, leveraging `Flow`.
*   The sample application is more robust, maintainable, and demonstrates a cleaner architecture using a ViewModel.
*   The UI of the sample app is more organized and user-friendly.
Copilot AI review requested due to automatic review settings September 8, 2025 12:33

This comment was marked as outdated.

This commit addresses an issue where the overall countdown timer for polling operations did not accurately reflect the paused state.

**Key Changes:**

*   **`PollingEngine.kt`:**
    *   The internal delay mechanism within the polling loop has been refined. Instead of a single `delay()` call, it now uses a `while` loop with smaller delay steps (`delayStep = 100L`).
    *   During each step, it checks if the poll is paused. If paused, it suspends execution using `control.state.map { it == State.Running }.first { it }` until resumed.
    *   This ensures that the overall timeout countdown effectively freezes when a poll is paused and resumes correctly when the poll is resumed.

*   **`PollingViewModel.kt` (Sample App):**
    *   The logic for identifying the `pollingSession` ID after starting a new poll has been made more robust.
    *   It now captures the set of active polling IDs before and after launching the new poll. The difference between these sets is used to identify the newly created session ID.
    *   Includes a short retry mechanism (`repeat(10) { delay(50) }`) to allow time for the new session to be registered in `Polling.listActiveIds()`.
    *   A fallback is added: if a unique ID cannot be determined via the diff, but there's exactly one active poll, that ID is used.
    *   Error logging is added if the session ID cannot be reliably determined, as this would affect pause/resume functionality in the sample app.
    *   The `startCountdown()` is now called after the session ID is likely established to ensure the countdown is associated with the correct polling operation.
    *   Added `try-catch` around the polling start logic to handle potential errors during initialization and update UI state accordingly.

*   **`PollingEngineCoreTests.kt`:**
    *   Removed an unused `outcome` variable read in `testMaxAttemptsReachedWithRetryFalse`.

**Functionality Improvements:**

*   The overall timeout countdown timer in the `PollingEngine` now accurately pauses when a polling operation is paused and resumes when it's resumed.
*   The sample application's `PollingViewModel` is more reliable in tracking the active polling session, improving the behavior of its pause/resume feature.
@bosankus bosankus requested a review from Copilot September 8, 2025 13:05
This commit updates the CodeQL workflow to improve analysis for Kotlin Multiplatform and Swift:

*   **Java/Kotlin Analysis:**
    *   Adds Java 17 (Temurin) setup using `actions/setup-java@v4` with Gradle caching.
    *   Replaces `codeql-action/autobuild` with a specific Gradle command (`./gradlew :pollingengine:compileKotlinMetadata`) to build only the common Kotlin metadata. This avoids unnecessary Android/iOS toolchain setup and potential conflicts during CodeQL's Java analysis.
*   **Swift Analysis:**
    *   Replaces `codeql-action/autobuild` with an explicit `xcodebuild` command to build the iOS app for the simulator. This ensures the Swift code is compiled in a way that CodeQL can effectively analyze, with `CODE_SIGNING_ALLOWED=NO` to avoid signing issues in CI.

These changes provide more targeted and reliable builds for CodeQL analysis, focusing on the relevant source code for each language.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces a public polling API facade and internalizes the engine implementation to provide a cleaner, more stable public interface for consumers. The refactoring focuses on simplifying the API surface while maintaining backward compatibility for core functionality.

  • Introduces Polling facade object as the primary entry point with PollingApi interface
  • Internalizes PollingEngine and related implementation details
  • Replaces Logger and Metrics interfaces with callback hooks for observability
  • Updates package coordinates from io.github.bosankus to in.androidplay

Reviewed Changes

Copilot reviewed 24 out of 27 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
PollingEngineCoreTests.kt New comprehensive test suite covering success, failure, timeout, and compose scenarios
PollingEngineCancellationTest.kt Updated to use new Polling.run API
BackoffPolicyTest.kt Reformatted test assertions for better readability
PollingConfigBuilder.kt New builder class with comprehensive documentation and validation
PollingSession.kt New lightweight session handle replacing internal Handle
PollingEngine.kt Internalized with Flow-based API and improved pause/resume logic
PollingAPI.kt New public interface defining stable API contract
Polling.kt New facade object implementing PollingApi and delegating to internal engine
Core.kt Internalized strategies and error codes, renamed DefaultRetryPredicates to RetryPredicates
App.kt Updated sample app to use new facade with comprehensive ViewModel implementation
Comments suppressed due to low confidence (1)

pollingengine/src/commonMain/kotlin/in/androidplay/pollingengine/polling/PollingConfigBuilder.kt:1

  • The hardcoded error code -1 is a magic number. Consider extracting this as a named constant to improve code maintainability and make the default behavior more explicit.
package `in`.androidplay.pollingengine.polling

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@bosankus bosankus merged commit c3cdaa6 into main Sep 9, 2025
1 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants