Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: injectable http client #112

Closed
wants to merge 17 commits into from
Closed

Conversation

mohnoor94
Copy link
Member

@mohnoor94 mohnoor94 commented Nov 28, 2024

Note

This is the final feature branch we've been working on to build the new SDK core module with injectable HTTP client interfaces and utilities. Changes here were reviewed in previous PRs.

#104 | #105 | #106 | #107 | #108 | #109 | #110

Overview

Working towards offering richer and more flexible SDK interfaces, this PR brings a completely new SDK core module implementation with key enhancements/features like:

  1. No direct dependency on any specific HTTP client.

  2. Providing out-of-the-box, fully configurable OkHttpClient as a default client.

  3. Getting rid of all Kotlin coroutines dependencies for better Java compatibility.

  4. Significantly reduced the number of dependencies and size

  5. Implemented SDK-specific HTTP models (e.g. Headers, Request, Response, etc...) to reach full independence of any 3rd parties implementations.

  6. Designed a new SDK-level interceptor chain

  7. Implemented a new bearer authentication package that can be used as an SDK interceptor.

  8. Cleaned up the old core package

The New SDK Core

The new core package can be viewed as a tool-box that product SDKs use to build the final experience. It's completely product-agnostic and it doesn't contain any product SDKs concerns.

We've defined a set of requirements this new core package should satisfy in order to achieve the final experience.

  1. The SDK should accept custom HTTP clients with minimal overhead on users.

  2. The SDK should be able to maintain its standard request-response flow even with the custom HTTP clients, this includes authentication, logging, serialization/deserialization.

  3. The SDK should provide a default HTTP client out-of-the-box. This default client should be configurable with options we see it's safe to expose to users.

  4. The SDK accepts either:

    • Completely custom HTTP client. This client should be pre-configured from by the user.
    • Custom configurations for the default client.

SDK HTTP Models

Problem?
The old core was coupled with 3rd party libraries (e.g. Ktor, OkHTTP, etc..). This made designing a unified interface that enables users injecting HTTP clients harder and forced us to follow the library's standards and conventions, limiting our control on the SDK behaviour.

Solution
Implementing HTTP models specific for the SDK like Request, Response, RequestBody By having such models, we're decoupling the SDK from any 3rd party implementation and makes it easier to design the adapter interface for HTTP clients injection.

Transport Interface

Problem?
To achieve complete independence between the SDK and the underlying HTTP client; the SDK shouldn't be aware of anything other than abstract interfaces. Therefore, we need one simple interface the SDK knows how to deal with, delegating the concrete implementations to users where they can use their custom HTTP client with the SDK by following the contract declared in the interface.

Solution
The Transport interface is introduced and expected to be implemented by users who wish to use their own HTTP clients with the SDK.

  1. This interface has one execute(request: Request): Response method that must be implemented.
  2. Users have to do the conversion logic between their client's HTTP models to the SDK HTTP models. Essentially, converting the SDK Request to the suitable request model expected by their HTTP client, execute the request, handle the response, and finally, convert the received response to the Response model expected by the SDK.

RequestExecutor Abstract Class

Problem?
The way we've been building the SDK is tightly coupled to the underlying HTTP client, so when it comes to custom HTTP clients injection, we need to abstract some aspects in the SDK like logging, authentication, serialization/deserialization.

Solution
The RequestExecutor is an abstract class that - mainly - wraps the Transport instance and executes the needed interceptors before/after the request execution, regardless of the underlying HTTP client.

Since we need a way to do these "interceptions" independently, we cannot rely on the HTTP client's native interceptors, therefore, a custom Interceptor interface is added to achieve similar experience to what we have at the moment, but completely managed by the SDK.

Product SDK Configurations

Problem?
Different configuration classes related to concrete product SDKs were maintained in the core package, which introduced a lot of complexity in the core module to differentiate between configuration classes needed by each product SDK.

Solution
Each product SDK should maintain its own configurations, interfaces, executors, and any other product-specific concerns. Keeping the core module only aware of lower-level HTTP configurations.
This design enables us to get rid of the added complexity in SDK configs (Providers, Collectors, Traits, etc..) The request is managed in the core module that only cares about the API endpoint and the HTTP client configurations (timeouts, connections).

The product SDK is expected to have:

  1. Its own variant of the client configuration classes/types exposed to the user.
  2. Its own version of the RequestExecutor mentioned above. Where each SDK define its own suitable interceptors, including authentication, logging, etc...

New Core - High-Level Package View

The following visualisation describes the set of tools the core module offers.

Screenshot 2024-12-03 at 00 06 04

OmarAlJarrah and others added 16 commits October 30, 2024 18:47
# Conflicts:
#	README.md
#	code/build.gradle
#	code/src/main/kotlin/com/expediagroup/sdk/graphql/common/DefaultGraphQLExecutor.kt
#	code/src/main/kotlin/com/expediagroup/sdk/graphql/common/GraphQLClient.kt
#	code/src/main/kotlin/com/expediagroup/sdk/graphql/common/GraphQLExecutor.kt
#	code/src/main/kotlin/com/expediagroup/sdk/graphql/model/response/Error.kt
#	code/src/main/kotlin/com/expediagroup/sdk/lodgingconnectivity/configuration/ClientConfiguration.kt
#	code/src/main/kotlin/com/expediagroup/sdk/lodgingconnectivity/payment/PaymentClient.kt
#	code/src/main/kotlin/com/expediagroup/sdk/lodgingconnectivity/sandbox/SandboxDataManagementClient.kt
#	code/src/main/kotlin/com/expediagroup/sdk/lodgingconnectivity/supply/reservation/ReservationClient.kt
#	gradle.properties
@Mohammad-Dwairi Mohammad-Dwairi marked this pull request as ready for review December 2, 2024 21:14
@Mohammad-Dwairi Mohammad-Dwairi requested a review from a team as a code owner December 2, 2024 21:14
@mohnoor94 mohnoor94 closed this Dec 3, 2024
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