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(clients): helper to switch API key in use #3616

Merged
merged 49 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
613e851
feat(csharp): implement helper
Fluf22 Aug 29, 2024
c90a8a4
feat(dart): switch api key helper
Fluf22 Aug 30, 2024
3bfe471
feat(go): switch api key helper
Fluf22 Aug 30, 2024
f324962
feat(php): switch api key helper
Fluf22 Sep 2, 2024
7cded73
feat(python): switch api key helper
Fluf22 Sep 2, 2024
f49a641
feat(swift): switch api key helper
Fluf22 Sep 2, 2024
0456016
feat(ruby): switch api key helper
Fluf22 Sep 2, 2024
e1f3f95
feat(scala): switch api key helper
Fluf22 Sep 2, 2024
f3ab40b
feat(kotlin): switch api key helper
Fluf22 Sep 2, 2024
6c32c71
feat(java): switch api key helper
Fluf22 Sep 2, 2024
e3ff9ee
wip
Fluf22 Sep 2, 2024
a106640
feat(javascript): switch api key helper
Fluf22 Sep 2, 2024
0c92997
fix(kotlin): redundant apiKey setter
Fluf22 Sep 3, 2024
44ada68
fix(clients): %s/setApiKey/setAlgoliaApiKey
Fluf22 Sep 3, 2024
437de4e
feat(cts): add test for the helper
Fluf22 Sep 4, 2024
12904fa
fix(csharp): update name
Fluf22 Sep 4, 2024
747c39f
fix: everything else
Fluf22 Sep 4, 2024
691959b
fix(go): handle error
Fluf22 Sep 5, 2024
34b7cc7
chore(go): remove unused method
Fluf22 Sep 5, 2024
f495bac
fix java
millotp Sep 5, 2024
d8e1039
fix(ruby): better dx
Fluf22 Sep 5, 2024
a092d46
fix(go): check err
Fluf22 Sep 5, 2024
372afa2
fix(python): naming
Fluf22 Sep 5, 2024
40bce91
ruby
millotp Sep 5, 2024
9bfdd39
Merge branch 'main' into fix/switch-api-key
millotp Sep 5, 2024
cbc6a2b
fix(javascript): docs
Fluf22 Sep 5, 2024
98703a4
chore: js is not working and I don't know why
Fluf22 Sep 5, 2024
31c12d3
fix(cssharp): shouldScope + better set key
Fluf22 Sep 5, 2024
9a97a33
fix dart
millotp Sep 5, 2024
47dd555
fix(python): at last
Fluf22 Sep 5, 2024
78e221b
scala
millotp Sep 5, 2024
004142f
requester is an interface
millotp Sep 5, 2024
cac83b0
fix(kotlin): done
Fluf22 Sep 5, 2024
58223e6
new line
millotp Sep 5, 2024
c150f91
fix: rename to setClientApiKey
Fluf22 Sep 5, 2024
1be1b29
fix: file renaming
Fluf22 Sep 5, 2024
70c1d65
test
millotp Sep 5, 2024
1928db8
fix template
millotp Sep 5, 2024
ddf1377
this is the end
millotp Sep 5, 2024
6a4f7cd
no response in snippets
millotp Sep 5, 2024
c3615f4
too much copy paste
millotp Sep 5, 2024
c83a12b
revert snippets
millotp Sep 5, 2024
9a02be0
snippet go no comment
millotp Sep 5, 2024
1804b5c
remove log from guides
millotp Sep 5, 2024
3e0abf1
final touches
millotp Sep 5, 2024
2311370
copilot incident
millotp Sep 6, 2024
9ba1977
fix(php): comments mixed up
Fluf22 Sep 6, 2024
76b9b76
Merge branch 'main' into fix/switch-api-key
millotp Sep 6, 2024
763b978
fix(ruby): formatting
Fluf22 Sep 6, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ protected AlgoliaConfig(string appId, string apiKey, string clientName, string c
/// The admin API Key
/// </summary>
/// <returns></returns>
public string ApiKey { get; }
public string ApiKey { get; set; }

/// <summary>
/// Configurations hosts
Expand Down Expand Up @@ -97,5 +97,16 @@ internal Dictionary<string, string> BuildHeaders()
DefaultHeaders[Defaults.UserAgentHeader.ToLowerInvariant()] = UserAgent.ToString();
return DefaultHeaders;
}

/// <summary>
/// Helper to switch the API key sent with each request
/// </summary>
/// <param name="apiKey">Your API Key</param>
/// <returns></returns>
public void SetClientApiKey(string apiKey)
{
ApiKey = apiKey;
DefaultHeaders[Defaults.AlgoliaApiKeyHeader.ToLowerInvariant()] = apiKey;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ internal class HttpTransport
private readonly IHttpRequester _httpClient;
private readonly ISerializer _serializer;
private readonly RetryStrategy _retryStrategy;
private readonly AlgoliaConfig _algoliaConfig;
internal AlgoliaConfig _algoliaConfig;
private string _errorMessage;
private readonly ILogger<HttpTransport> _logger;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,12 @@ import 'package:algolia_client_core/src/config/client_options.dart';

/// An abstract class representing an API client with specific properties and options.
abstract interface class ApiClient {
/// The unique identifier for the application using the API client.
String get appId;

/// The API key used for authentication.
String get apiKey;

/// A set of custom client options to configure the behavior of the API client.
ClientOptions get options;

/// Allow switching the API key used to authenticate requests.
void setClientApiKey({required String apiKey});

/// Dispose of underlying resources.
void dispose();
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class AuthInterceptor extends Interceptor {
final String appId;

/// The API key used for Algolia authentication.
final String apiKey;
String apiKey;

/// Constructs an [AuthInterceptor] with the provided application id and API key.
AuthInterceptor({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import 'package:dio/dio.dart';
/// response conversion and error handling.
class DioRequester implements Requester {
/// The underlying Dio client.
final Dio _client;
final AuthInterceptor _authInterceptor;
late final Dio _client;

/// Constructs a [DioRequester] with the given [appId], [apiKey], and [options].
DioRequester({
Expand All @@ -28,29 +29,30 @@ class DioRequester implements Requester {
Function(Object?)? logger,
Iterable<Interceptor>? interceptors,
HttpClientAdapter? httpClientAdapter,
}) : _client = Dio(
BaseOptions(
headers: headers,
connectTimeout: connectTimeout,
}) : _authInterceptor = AuthInterceptor(
appId: appId,
apiKey: apiKey,
) {
_client = Dio(
BaseOptions(
headers: headers,
connectTimeout: connectTimeout,
),
)..interceptors.addAll([
_authInterceptor,
AgentInterceptor(
agent: AlgoliaAgent(packageVersion)
..addAll(clientSegments ?? const [])
..addAll(Platform.agentSegments()),
),
if (logger != null)
LogInterceptor(
requestBody: true,
responseBody: true,
logPrint: logger,
),
)..interceptors.addAll([
AuthInterceptor(
appId: appId,
apiKey: apiKey,
),
AgentInterceptor(
agent: AlgoliaAgent(packageVersion)
..addAll(clientSegments ?? const [])
..addAll(Platform.agentSegments()),
),
if (logger != null)
LogInterceptor(
requestBody: true,
responseBody: true,
logPrint: logger,
),
if (interceptors != null) ...interceptors,
]) {
if (interceptors != null) ...interceptors,
]);
if (httpClientAdapter != null) {
_client.httpClientAdapter = httpClientAdapter;
}
Expand Down Expand Up @@ -114,4 +116,9 @@ class DioRequester implements Requester {

@override
void close() => _client.close();

@override
void setClientApiKey(String apiKey) {
_authInterceptor.apiKey = apiKey;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ abstract class Requester {
/// The method returns a Future that resolves to an [HttpResponse].
Future<HttpResponse> perform(HttpRequest request);

/// Allows to switch the API key used to authenticate requests.
void setClientApiKey(String apiKey);

/// Closes any underlying resources that the Requester might be using.
///
/// By default, it does nothing (no-op), but it can be implemented to handle resource cleanup
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nonnull;

/**
* Represents a base client for making API requests. The client uses a {@link Requester} for
Expand All @@ -26,6 +27,7 @@ public abstract class ApiClient implements Closeable {

private final Requester requester;
private final ExecutorService executor;
private AuthInterceptor authInterceptor;

/** Constructs a new instance of the {@link ApiClient}. */
protected ApiClient(String appId, String apiKey, String clientName, @Nullable ClientOptions options, List<Host> defaultHosts) {
Expand All @@ -52,8 +54,9 @@ private Requester defaultRequester(String appId, String apiKey, String clientNam
List<StatefulHost> statefulHosts = hosts.stream().map(StatefulHost::new).collect(Collectors.toList());

JsonSerializer serializer = JsonSerializer.builder().setCustomConfig(options.getMapperConfig()).build();
this.authInterceptor = new AuthInterceptor(appId, apiKey);
HttpRequester.Builder builder = new HttpRequester.Builder(serializer)
.addInterceptor(new AuthInterceptor(appId, apiKey))
.addInterceptor(authInterceptor)
.addInterceptor(new UserAgentInterceptor(algoliaAgent))
.addInterceptor(new RetryStrategy(statefulHosts));
if (options.getRequesterConfig() != null) {
Expand All @@ -62,6 +65,15 @@ private Requester defaultRequester(String appId, String apiKey, String clientNam
return builder.build(options);
}

/**
* Helper method to switch the API key used to authenticate the requests.
*
* @param apiKey The new API key to be used from now on.
*/
public void setClientApiKey(@Nonnull String apiKey) {
this.authInterceptor.setApiKey(apiKey);
}

/**
* Executes an HTTP request asynchronously and returns a {@link CompletableFuture} of the response
* deserialized into a specified type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@ public final class AuthInterceptor implements Interceptor {
private static final String HEADER_API_KEY = "x-algolia-api-key";

private final String applicationId;
private final String apiKey;
private String apiKey;

public AuthInterceptor(String applicationId, String apiKey) {
this.applicationId = applicationId;
this.apiKey = apiKey;
}

public void setApiKey(String apiKey) {
this.apiKey = apiKey;
}

@Nonnull
@Override
public Response intercept(Chain chain) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import io.ktor.client.request.*
import io.ktor.serialization.kotlinx.json.*

private const val HEADER_APPLICATION_ID = "x-algolia-application-id"
private const val HEADER_APIKEY = "x-algolia-api-key"
public const val HEADER_APIKEY: String = "x-algolia-api-key"

internal fun algoliaHttpClient(
appId: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,6 @@ public interface Requester {
requestOptions: RequestOptions? = null,
returnType: TypeInfo,
): T

public fun setClientApiKey(apiKey: String);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.algolia.client.transport.internal

import com.algolia.client.configuration.CallType
import com.algolia.client.configuration.Host
import com.algolia.client.configuration.internal.HEADER_APIKEY
import com.algolia.client.exception.AlgoliaRetryException
import com.algolia.client.exception.internal.asApiException
import com.algolia.client.exception.internal.asClientException
Expand Down Expand Up @@ -38,6 +39,15 @@ public class KtorRequester(
private val mutex: Mutex = Mutex()
private val retryableHosts = hosts.map { RetryableHost(it) }

public override fun setClientApiKey(apiKey: String) {
headers {
if (contains(HEADER_APIKEY)) {
remove(HEADER_APIKEY)
}
append(HEADER_APIKEY, apiKey)
}
}

override suspend fun <T> execute(
requestConfig: RequestConfig,
requestOptions: RequestOptions?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public function __construct(array $config = [])
throw new AlgoliaException('`apiKey` is missing.');
}

$this->setAlgoliaApiKey($config['apiKey']);
$this->setClientApiKey($config['apiKey']);
$this->setAuth('x-algolia-api-key', $config['apiKey']);

$this->setAppId($config['appId']);
Expand Down Expand Up @@ -193,13 +193,6 @@ public function getAlgoliaApiKey()
return $this->config['apiKey'];
}

public function setAlgoliaApiKey($apiKey)
{
$this->config['apiKey'] = $apiKey;

return $this;
}

public function getHosts()
{
return $this->config['hosts'];
Expand Down Expand Up @@ -272,6 +265,30 @@ public function setDefaultHeaders(array $defaultHeaders)
return $this;
}

/**
* Switch the API key used to authenticate requessts.
*
* @param string $apiKey The new API key to be used
*
* @return void
*/
public function setClientApiKey($apiKey)
{
$this->config['apiKey'] = $apiKey;

return $this;
}

/**
* @deprecated This method is deprecated. Use setClientApiKey() instead.
*
* @param mixed $apiKey
*/
public function setAlgoliaApiKey($apiKey)
{
return $this->setClientApiKey($apiKey);
}

/**
* Sets the user agent of the api client.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,8 @@ def __init__(self, app_id: Optional[str] = None, api_key: Optional[str] = None):
self.headers = None
self.proxies = None
self.hosts = None

def set_client_api_key(self, api_key: str) -> None:
"""Sets a new API key to authenticate requests."""
self.api_key = api_key
self.headers["x-algolia-api-key"] = api_key
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ def self.default
@@default ||= ApiClient.new
end

def set_client_api_key(api_key)
@config.set_client_api_key(api_key)
end

# Call an API with given options.
#
# @return [Http::Response] the response.
Expand Down Expand Up @@ -47,7 +51,7 @@ def deserialize(body, return_type)
return body.to_s if return_type == "String"

begin
data = JSON.parse("[#{body}]", :symbolize_names => true)[0]
data = JSON.parse("[#{body}]", symbolize_names: true)[0]
rescue JSON::ParserError => e
raise e unless %w[String Date Time].include?(return_type)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ def initialize(app_id, api_key, hosts, client_name, opts = {})
yield(self) if block_given?
end

def set_client_api_key(api_key)
@api_key = api_key
@header_params["X-Algolia-API-Key"] = api_key
end

# The default Configuration object.
def self.default
@@default ||= Configuration.new
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ abstract class ApiClient(
throw AlgoliaClientException("`apiKey` is missing.")
}

private val authInterceptor = new AuthInterceptor(appId, apiKey)

private val requester = options.customRequester match {
case Some(customRequester) => customRequester
case None =>
Expand All @@ -62,7 +64,7 @@ abstract class ApiClient(

val builder = HttpRequester
.builder(options.customFormats.getOrElse(formats))
.withInterceptor(new AuthInterceptor(appId, apiKey))
.withInterceptor(authInterceptor)
.withInterceptor(new UserAgentInterceptor(algoliaAgent))
.withInterceptor(new RetryStrategy(statefulHosts))

Expand All @@ -89,4 +91,8 @@ abstract class ApiClient(
override def close(): Unit = {
Try(requester.close())
}

def setClientApiKey(apiKey: String): Unit = {
authInterceptor.setApiKey(apiKey)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@ import okhttp3.{Interceptor, Response}
* API key
*/
private[algoliasearch] class AuthInterceptor(
applicationId: String,
apiKey: String
applicationId: String,
private var apiKey: String
) extends Interceptor {

private val HeaderApplicationId = "x-algolia-application-id"
private val HeaderApiKey = "x-algolia-api-key"

def setApiKey(newApiKey: String): Unit = {
apiKey = newApiKey
}

override def intercept(chain: Interceptor.Chain): Response = {
val originalRequest = chain.request()
val builder = originalRequest.newBuilder()
Expand Down
Loading
Loading