Skip to content

Commit

Permalink
Merge pull request #494 from microsoft/rsh/Url_replace_handler
Browse files Browse the repository at this point in the history
Rsh/url replace handler
  • Loading branch information
baywet authored Jul 20, 2023
2 parents 7b40a3d + db43ebf commit 0162d03
Show file tree
Hide file tree
Showing 5 changed files with 253 additions and 1 deletion.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

## [0.4.7] - 2023-07-21

### Added

- Adds the `UrlReplaceHandler` middleware to the Okhttp component to allow for customizing the URL before sending the request.

## [0.4.6] - 2023-07-20

### Added
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package com.microsoft.kiota.http.middleware;

import com.microsoft.kiota.http.middleware.options.UrlReplaceHandlerOption;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;

import javax.annotation.Nonnull;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Map;
import java.util.Objects;

/**
* A middleware to replace the url with the specified replacement pairs.
*/
public class UrlReplaceHandler implements Interceptor {

private UrlReplaceHandlerOption mUrlReplaceHandlerOption;

/**
* Instantiate a UrlReplaceHandler with default UrlReplaceHandlerOption.
*/
public UrlReplaceHandler(){
this(new UrlReplaceHandlerOption());
}
/**
* Instantiate a UrlReplaceHandler with specified UrlReplaceHandlerOption
* @param urlReplaceHandlerOption the specified UrlReplaceHandlerOption for the UrlReplaceHandler.
*/
public UrlReplaceHandler(@Nonnull UrlReplaceHandlerOption urlReplaceHandlerOption){
Objects.requireNonNull(urlReplaceHandlerOption);
this.mUrlReplaceHandlerOption = new UrlReplaceHandlerOption(urlReplaceHandlerOption.getReplacementPairs(), urlReplaceHandlerOption.isEnabled());
}
/** {@inheritDoc} */
@Nonnull
@Override
public Response intercept(@Nonnull Chain chain) throws IOException {
Objects.requireNonNull(chain, "parameter chain cannot be null");
Request request = Objects.requireNonNull(chain.request(), "request cannot be null");
UrlReplaceHandlerOption replaceOption = request.tag(UrlReplaceHandlerOption.class);
replaceOption = replaceOption == null ? mUrlReplaceHandlerOption : replaceOption;
if(!replaceOption.isEnabled() || replaceOption.getReplacementPairs().isEmpty()) {
return chain.proceed(request);
}

final Span span = ObservabilityHelper.getSpanForRequest(request, "UrlReplaceHandler_Intercept");
Scope scope = null;
if (span != null) {
scope = span.makeCurrent();
span.setAttribute("com.microsoft.kiota.handler.urlreplace.enable", true);
}
try{
request = replaceRequestUrl(request, replaceOption.getReplacementPairs());
} finally {
if (scope != null) {
scope.close();
}
if (span != null) {
span.end();
}
}
return chain.proceed(request);
}
/**
* Gets the UrlReplaceHandlerOption for the UrlReplaceHandler.
* @return the UrlReplaceHandlerOption for the UrlReplaceHandler.
*/
@Nonnull
public UrlReplaceHandlerOption getUrlReplaceHandlerOption() {
return new UrlReplaceHandlerOption(mUrlReplaceHandlerOption.getReplacementPairs(), mUrlReplaceHandlerOption.isEnabled());
}
/**
* Sets the UrlReplaceHandlerOption for the UrlReplaceHandler.
* @param urlReplaceHandlerOption the UrlReplaceHandlerOption to set.
*/
public void setUrlReplaceHandlerOption(@Nonnull UrlReplaceHandlerOption urlReplaceHandlerOption) {
this.mUrlReplaceHandlerOption = new UrlReplaceHandlerOption(urlReplaceHandlerOption.getReplacementPairs(), urlReplaceHandlerOption.isEnabled());
}
/**
* Replaces the url of the request using the replacement pairs provided.
* @param request the request to replace the url of.
* @param replacementPairs the replacement pairs to use.
* @return the request with the updated url.
*/
@Nonnull
public static Request replaceRequestUrl(@Nonnull Request request, @Nonnull Map<String, String> replacementPairs) {
Request.Builder builder = request.newBuilder();
try {
//Decoding the url since Request.url is encoded by default.
String replacedUrl = URLDecoder.decode(request.url().toString(), "UTF-8");//Using decode(String,String) method to maintain source compatibility with Java 8
for (Map.Entry<String, String> entry : replacementPairs.entrySet()) {
replacedUrl = replacedUrl.replace(entry.getKey(), entry.getValue());
}
builder.url(replacedUrl);
return builder.build();

} catch (UnsupportedEncodingException e) {
return request; //This should never happen since "UTF-8" is a supported encoding.
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.microsoft.kiota.http.middleware.options;

import com.microsoft.kiota.RequestOption;

import javax.annotation.Nonnull;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
* The options to be passed to the UrlReplaceHandler.
* Defines the replacement pairs and whether the handler is enabled or not.
*/
public class UrlReplaceHandlerOption implements RequestOption {

private Map<String, String> replacementPairs;
private boolean enabled;

/**
* Instantiates a new UrlReplaceOption with an empty replacementPairs map and enabled set to true.
*/
public UrlReplaceHandlerOption() {
this(new HashMap<>());
}
/**
* Instantiates a new UrlReplaceOption with the specified replacementPairs map and enabled set to true.
* @param replacementPairs the replacement pairs map.
*/
public UrlReplaceHandlerOption(@Nonnull Map<String, String> replacementPairs) {
this(replacementPairs, true);
}
/**
* Instantiates a new UrlReplaceOption with the specified replacementPairs map and enabled set to the specified value.
* @param enabled whether the handler is enabled or not.
* @param replacementPairs the replacement pairs map.
*/
public UrlReplaceHandlerOption(@Nonnull Map<String, String> replacementPairs, boolean enabled) {
Objects.requireNonNull(replacementPairs);
this.replacementPairs = new HashMap<>(replacementPairs);
this.enabled = enabled;
}
/**
* Gets the replacement pairs map.
* @return the replacement pairs map.
*/
@Nonnull
public Map<String, String> getReplacementPairs() {
return new HashMap<>(replacementPairs);
}
/**
* Sets the replacement pairs map.
* @param replacementPairs the replacement pairs map.
*/
public void setReplacementPairs(@Nonnull final Map<String, String> replacementPairs) {
this.replacementPairs = new HashMap<>(replacementPairs);
}
/**
* Enables the handler.
*/
public void enable() {
this.enabled = true;
}
/**
* Disables the handler.
*/
public void disable() {
this.enabled = false;
}
/**
* Gets whether the handler is enabled or not.
* @return whether the handler is enabled or not.
*/
public boolean isEnabled() {
return enabled;
}
@Nonnull
@SuppressWarnings("unchecked")
@Override
public <T extends RequestOption> Class<T> getType() {
return (Class<T>) UrlReplaceHandlerOption.class;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.microsoft.kiota.http;

import com.microsoft.kiota.http.middleware.UrlReplaceHandler;
import com.microsoft.kiota.http.middleware.options.UrlReplaceHandlerOption;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.util.HashMap;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

class UrlReplaceHandlerTest {

private final static String defaultUsersWithTokenUrl = "https://graph.microsoft.com/v1.0/users/TokenToReplace";
private final static HashMap<String, String> defaultReplacementPairs = new HashMap<>();

@Test
void testUrlReplaceHandler_no_replacementPairs() throws IOException {
Interceptor[] interceptors = new Interceptor[]{new UrlReplaceHandler(new UrlReplaceHandlerOption())};
final OkHttpClient client = KiotaClientFactory.create(interceptors).build();
final Request request = new Request.Builder().url(defaultUsersWithTokenUrl).build();
final Response response = client.newCall(request).execute();

assertNotNull(response);
assertEquals(defaultUsersWithTokenUrl, response.request().url().toString()); //url should remain the same without replacement pairs

}
@Test
void testUrlReplaceHandler_default_url() throws IOException {
defaultReplacementPairs.put("/users/TokenToReplace", "/me");
Interceptor[] interceptors = new Interceptor[]{new UrlReplaceHandler(new UrlReplaceHandlerOption(defaultReplacementPairs))};
final OkHttpClient client = KiotaClientFactory.create(interceptors).build();
final Request request = new Request.Builder().url(defaultUsersWithTokenUrl).build();
final Response response = client.newCall(request).execute();
final String expectedNewUrl = "https://graph.microsoft.com/v1.0/me";

assertNotNull(response);
assertEquals(expectedNewUrl, response.request().url().toString());
}
@Test
void testUrlReplaceHandler_multiple_pairs() throws IOException {
defaultReplacementPairs.put("/users/TokenToReplace", "/me");
defaultReplacementPairs.put("{secondToken}", "expectedValue");
String customUrl = "https://graph.microsoft.com/beta/users/TokenToReplace/{secondToken}"; //using special characters to test decoding.
Interceptor[] interceptors = new Interceptor[]{new UrlReplaceHandler(new UrlReplaceHandlerOption(defaultReplacementPairs))};
final OkHttpClient client = KiotaClientFactory.create(interceptors).build();
final Request request = new Request.Builder().url(customUrl).build();
final Response response = client.newCall(request).execute();
final String expectedNewUrl = "https://graph.microsoft.com/beta/me/expectedValue";

assertNotNull(response);
assertEquals(expectedNewUrl, response.request().url().toString());
}
}
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ org.gradle.caching=true
mavenGroupId = com.microsoft.kiota
mavenMajorVersion = 0
mavenMinorVersion = 4
mavenPatchVersion = 6
mavenPatchVersion = 7
mavenArtifactSuffix =

#These values are used to run functional tests
Expand Down

0 comments on commit 0162d03

Please sign in to comment.