Skip to content

Commit

Permalink
Support RpcHttpBodyOnly and RpcHttpTriggerMetadataRemoved
Browse files Browse the repository at this point in the history
  • Loading branch information
amamounelsayed committed May 6, 2020
1 parent eb1d717 commit fbdc0dd
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 5 deletions.
15 changes: 15 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@
<artifactId>jna-platform</artifactId>
<version>5.3.0</version>
</dependency>
<dependency>
<groupId>org.jmockit</groupId>
<artifactId>jmockit</artifactId>
<version>1.49</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down Expand Up @@ -237,6 +243,15 @@
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version> <!-- or some other version -->
<configuration>
<argLine>
-javaagent:"${settings.localRepository}"\org\jmockit\jmockit\1.49\jmockit-1.49.jar
</argLine>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.microsoft.azure.functions.worker;

/**
* The Constants file for Java language worker
*/
public final class Constants {
private Constants(){}
public final static String TRIGGER_METADATA_DOLLAR_REQUEST_KEY = "$request";
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

import com.microsoft.azure.functions.rpc.messages.*;
import com.microsoft.azure.functions.worker.Constants;
import com.microsoft.azure.functions.worker.binding.BindingDataStore;
import com.microsoft.azure.functions.worker.description.FunctionMethodDescriptor;
import com.microsoft.azure.functions.worker.reflect.ClassLoaderProvider;
Expand Down Expand Up @@ -46,7 +45,7 @@ public Optional<TypedData> invokeMethod(String id, InvocationRequest request, Li

BindingDataStore dataStore = new BindingDataStore();
dataStore.setBindingDefinitions(executor.getBindingDefinitions());
dataStore.addTriggerMetadataSource(request.getTriggerMetadataMap());
dataStore.addTriggerMetadataSource(getTriggerMetadataMap(request));
dataStore.addParameterSources(request.getInputDataList());
dataStore.addExecutionContextSource(request.getInvocationId(), methodEntry.left, request.getTraceContext().getTraceParent(), request.getTraceContext().getTraceState(), request.getTraceContext().getAttributesMap());

Expand All @@ -59,6 +58,29 @@ public Optional<String> getMethodName(String id) {
return Optional.ofNullable(this.methods.get(id)).map(entry -> entry.left);
}

// TODO the scope should be package private for testability. Modify the package name as same as main package
public Map<String, TypedData> getTriggerMetadataMap(InvocationRequest request) {
String name ="";
TypedData dataWithHttp = null;
for(ParameterBinding e: request.getInputDataList()) {
if (e.getData().hasHttp()) {
dataWithHttp = e.getData();
name = e.getName();
break;
}
}

Map<String, TypedData> triggerMetadata = new HashMap(request.getTriggerMetadataMap());
if (!name.isEmpty() && !triggerMetadata.containsKey(name)) {
triggerMetadata.put(name, dataWithHttp);
}
String requestKey = Constants.TRIGGER_METADATA_DOLLAR_REQUEST_KEY;
if (dataWithHttp != null & !triggerMetadata.containsKey(requestKey)) {
triggerMetadata.put(requestKey, dataWithHttp);
}
return Collections.unmodifiableMap(triggerMetadata);
}

private void addSearchPathsToClassLoader(FunctionMethodDescriptor function) throws IOException {
URL jarUrl = new File(function.getJarPath()).toURI().toURL();
classLoaderProvider.addUrl(jarUrl);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ String execute(WorkerInitRequest request, WorkerInitResponse.Builder response) {
response.setWorkerVersion(Application.version());
response.putCapabilities("TypedDataCollection", "TypedDataCollection");
response.putCapabilities("WorkerStatus", "WorkerStatus");
response.putCapabilities("RpcHttpBodyOnly", "RpcHttpBodyOnly");
response.putCapabilities("RpcHttpTriggerMetadataRemoved", "RpcHttpTriggerMetadataRemoved");
return "Worker initialized";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package com.microsoft.azure.functions.worker.broker.tests;

import com.microsoft.azure.functions.rpc.messages.InvocationRequest;
import com.microsoft.azure.functions.rpc.messages.ParameterBinding;
import com.microsoft.azure.functions.rpc.messages.TypedData;
import com.microsoft.azure.functions.worker.broker.JavaFunctionBroker;
import com.microsoft.azure.functions.worker.reflect.DefaultClassLoaderProvider;
import mockit.*;
import org.junit.Test;
import java.util.*;

import static org.junit.Assert.assertEquals;

public class JavaFunctionBrokerTest {

@Test
public void getTriggerMetadataMap_success(
@Mocked InvocationRequest request,
@Mocked ParameterBinding binding,
@Mocked TypedData bindingData,
@Mocked TypedData name,
@Mocked TypedData query,
@Mocked TypedData headers,
@Mocked TypedData sys
) throws Exception {

String expectedData = "http {\n method: \"GET\"\n url: \"https://localhost:5001/api/HttpExample?name=ushio\"\n headers {\n key: \"cache-control\"\n value: \"max-age=0\"\n }\n headers {\n key: \"connection\"\n value: \"Keep-Alive\"\n }\n headers {\n key: \"accept\"\n value: \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\"\n }\n headers {\n key: \"accept-encoding\"\n value: \"gzip, deflate, br\"\n }\n headers {\n key: \"accept-language\"\n value: \"en-US,ja;q=0.8,en-GB;q=0.5,en;q=0.3\"\n }\n headers {\n key: \"host\"\n value: \"localhost:5001\"\n }\n headers {\n key: \"user-agent\"\n value: \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19041\"\n }\n headers {\n key: \"upgrade-insecure-requests\"\n value: \"1\"\n }\n query {\n key: \"name\"\n value: \"ushio\"\n }\n identities {\n name_claim_type {\n value: \"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name\"\n }\n role_claim_type {\n value: \"http://schemas.microsoft.com/ws/2008/06/identity/claims/role\"\n }\n }\n}\n";
String expectedName = "req";
new Expectations() {{
bindingData.hasHttp(); result = true;
bindingData.getString(); result = expectedData;
binding.getName(); result = expectedName;
binding.getData(); result = bindingData;
request.getInputDataList(); result = Arrays.asList(binding);

name.getString(); result = "string: \"John\"\n"; minTimes = 0;
query.getString(); result = "json: \"{\"name\":\"ushio\"}\""; minTimes = 0;
headers.getString(); result = "json: \"{\"Cache-Control\":\"max-age=0\",\"Connection\":\"Keep-Alive\",\"Accept\":\"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\",\"Accept-Encoding\":\"gzip, deflate, br\",\"Accept-Language\":\"en-US,ja;q=0.8,en-GB;q=0.5,en;q=0.3\",\"Host\":\"localhost:5001\",\"User-Agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19041\",\"Upgrade-Insecure-Requests\":\"1\"}\""; minTimes = 0;
sys.getString(); result = "json: \"{\"MethodName\":\"HttpExample\",\"UtcNow\":\"2020-04-30T15:26:57.281277Z\",\"RandGuid\":\"cd332c4a-df9e-415a-acd4-973994072e46\"}\""; minTimes = 0;

Map<String,TypedData> triggerMetadata = new HashMap<String, TypedData>();
triggerMetadata.put("name", name);
triggerMetadata.put("Query", query);
triggerMetadata.put("Headers", headers);
triggerMetadata.put("sys", sys);
request.getTriggerMetadataMap(); result = Collections.unmodifiableMap(triggerMetadata);
}};

JavaFunctionBroker broker = new JavaFunctionBroker(new DefaultClassLoaderProvider());
Map<String, TypedData> actualTriggerMetadata = broker.getTriggerMetadataMap(request);
TypedData actual = actualTriggerMetadata.get("$request");
assertEquals(actual.getString(), expectedData);
TypedData actual2 = actualTriggerMetadata.get(expectedName);
assertEquals(actual2.getString(), expectedData);
}

@Test
public void getTriggerMetadataMap_ignored(
@Mocked InvocationRequest request,
@Mocked ParameterBinding binding,
@Mocked TypedData bindingData,
@Mocked TypedData queueTrigger,
@Mocked TypedData dequeueCount,
@Mocked TypedData expirationTime,
@Mocked TypedData id,
@Mocked TypedData insertionTime,
@Mocked TypedData nextVisibleTime,
@Mocked TypedData popReceipt,
@Mocked TypedData sys
) throws Exception {

String data = "string: \"hello queue\"\n";
String name = "msg";
new Expectations() {{
bindingData.hasHttp(); result = false;
bindingData.getString(); result = data; minTimes = 0;
binding.getName(); result = name; minTimes = 0;
binding.getData(); result = bindingData; minTimes = 0;
request.getInputDataList(); result = Arrays.asList(binding);

queueTrigger.getString(); result = "string: \"hello queue\"\n"; minTimes = 0;
dequeueCount.getString(); result = "json: \"1\"\n"; minTimes = 0;
expirationTime.getString(); result = "json: \"\"2020-05-08T17:47:22+00:00\"\n"; minTimes = 0;
id.getString(); result = "string: \"e4f4a332-df80-41a1-8ecd-c2f7fba91f28\"\n"; minTimes = 0;
insertionTime.getString(); result = "json: \"\"2020-05-01T17:47:22+00:00\"\n"; minTimes = 0;
nextVisibleTime.getString(); result = "json: \"\"2020-05-01T17:57:22+00:00\"\n"; minTimes = 0;
popReceipt.getString(); result = "string: \"oJCJGfnt1wgBAAAA\"\n"; minTimes = 0;
sys.getString(); result = "json: \"{\"MethodName\":\"QueueProcessor\",\"UtcNow\":\"2020-05-01T17:47:29.2664174Z\",\"RandGuid\":\"7f67ac1c-b7b0-43f5-a51a-73af321d7d9f\"}\n"; minTimes = 0;

Map<String,TypedData> triggerMetadata = new HashMap<String, TypedData>();
triggerMetadata.put("QueueTrigger", queueTrigger);
triggerMetadata.put("DequeueCount", dequeueCount);
triggerMetadata.put("ExpirationTime", expirationTime);
triggerMetadata.put("Id", id);
triggerMetadata.put("InsertionTime", insertionTime);
triggerMetadata.put("NextVisibleTime", nextVisibleTime);
triggerMetadata.put("PopReceipt", popReceipt);
triggerMetadata.put("sys", sys);
request.getTriggerMetadataMap(); result = Collections.unmodifiableMap(triggerMetadata);
}};

int expectedCount = request.getTriggerMetadataMap().size();
JavaFunctionBroker broker = new JavaFunctionBroker(new DefaultClassLoaderProvider());
Map<String, TypedData> actualTriggerMetadata = broker.getTriggerMetadataMap(request);
// In case of non-http request, it will not modify the triggerMetadata
assertEquals(expectedCount, actualTriggerMetadata.size());
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.microsoft.azure.functions.worker.test.utilities;

import java.io.*;
import java.net.ServerSocket;
import java.util.*;
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.*;
Expand All @@ -17,11 +18,23 @@
import org.apache.commons.lang3.tuple.*;

public final class FunctionsTestHost implements AutoCloseable, IApplication {
private int port;
public FunctionsTestHost() throws Exception {
this.port = populatePort();
this.initializeServer();
this.initializeClient();
}

private final List<Integer> list = Arrays.asList(55005, 5005);

private int populatePort() {
try (ServerSocket ignored = new ServerSocket(this.list.get(0))) {
return this.list.get(0);
} catch (IOException e) {
return this.list.get(1);
}
}

@PostConstruct
private void initializeServer() throws IOException {
ServerBuilder<?> builder = ServerBuilder.forPort(this.getPort());
Expand Down Expand Up @@ -65,7 +78,7 @@ public final InvocationResponse call(String reqId, String funcId, Triple<String,
@Override
public String getHost() { return "localhost"; }
@Override
public int getPort() { return 55005; }
public int getPort() { return this.port; }
@Override
public Integer getMaxMessageSize() { return null; }

Expand Down

0 comments on commit fbdc0dd

Please sign in to comment.