From 5b84ae0d7ecd00492fa971975a28461c804d6fda Mon Sep 17 00:00:00 2001 From: Kamesh Sampath Date: Thu, 8 Feb 2018 14:21:09 +0530 Subject: [PATCH] Resolved Issue #141 --- ...n.java => OpenWhiskActionApplication.java} | 4 +- ...oller.java => OpenWhiskActionHandler.java} | 70 ++++++--- ...quest.java => OpenWhiskActionRequest.java} | 2 +- ...java => OpenWhiskFunctionInitializer.java} | 20 ++- ...Request.java => OpenWhiskInitRequest.java} | 2 +- .../openwhisk/OpenWhiskActionHandlerTest.java | 140 ++++++++++++++++++ .../resources/application-test.properties | 2 + 7 files changed, 202 insertions(+), 38 deletions(-) rename spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/{ActionApplication.java => OpenWhiskActionApplication.java} (90%) rename spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/{ActionController.java => OpenWhiskActionHandler.java} (50%) rename spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/{ActionRequest.java => OpenWhiskActionRequest.java} (98%) rename spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/{FunctionInitializer.java => OpenWhiskFunctionInitializer.java} (85%) rename spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/{InitRequest.java => OpenWhiskInitRequest.java} (96%) create mode 100644 spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/test/java/org/springframework/cloud/function/adapter/openwhisk/OpenWhiskActionHandlerTest.java create mode 100644 spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/test/resources/application-test.properties diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/ActionApplication.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/OpenWhiskActionApplication.java similarity index 90% rename from spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/ActionApplication.java rename to spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/OpenWhiskActionApplication.java index 6af423e43..456975d0d 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/ActionApplication.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/OpenWhiskActionApplication.java @@ -25,9 +25,9 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties */ @SpringBootApplication @EnableConfigurationProperties(FunctionProperties.class) -public class ActionApplication { +public class OpenWhiskActionApplication { public static void main(String[] args) { - SpringApplication.run(ActionApplication.class, args); + SpringApplication.run(OpenWhiskActionApplication.class, args); } } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/ActionController.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/OpenWhiskActionHandler.java similarity index 50% rename from spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/ActionController.java rename to spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/OpenWhiskActionHandler.java index 2ee448028..14a99b8eb 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/ActionController.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/OpenWhiskActionHandler.java @@ -16,48 +16,49 @@ package org.springframework.cloud.function.adapter.openwhisk; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; - import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; - +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; - import reactor.core.publisher.Flux; +import java.util.*; + /** * @author Mark Fisher + * @author Kamesh Sampath */ @RestController -public class ActionController extends FunctionInitializer { +public class OpenWhiskActionHandler extends OpenWhiskFunctionInitializer { - private final ObjectMapper objectMapper = new ObjectMapper(); + private static final String NO_INPUT_PROVIDED = "No input provided"; + private static Log logger = LogFactory.getLog(OpenWhiskFunctionInitializer.class); - public ActionController() { + @Autowired + private ObjectMapper mapper; + + public OpenWhiskActionHandler() { super(); } @PostMapping("/init") - public void init(@RequestBody InitRequest request) { + public void init(@RequestBody OpenWhiskInitRequest request) { initialize(); } - @PostMapping(value="/run", consumes="application/json", produces="application/json") - public Object run(@RequestBody ActionRequest request) { + @PostMapping(value = "/run", consumes = "application/json", produces = "application/json") + public Object run(@RequestBody OpenWhiskActionRequest request) { Object input = convertEvent(request.getValue()); - Flux output = apply(extract(input)); - Object result = result(input, output); - try { - return "{\"result\":" + this.objectMapper.writeValueAsString(result) + "}"; - } - catch (JsonProcessingException e) { - throw new IllegalStateException("failed to write JSON response", e); + Object result = NO_INPUT_PROVIDED; + if(input !=null ) { + Flux output = apply(extract(input)); + result = result(input, output); } + return serializeBody(result); } private Object result(Object input, Flux output) { @@ -65,7 +66,7 @@ public class ActionController extends FunctionInitializer { for (Object value : output.toIterable()) { result.add(value); } - if (isSingleValue(input) && result.size()==1) { + if (isSingleValue(input) && result.size() == 1) { return result.get(0); } return result; @@ -82,8 +83,31 @@ public class ActionController extends FunctionInitializer { return Flux.just(input); } - protected Object convertEvent(Map event) { + protected Object convertEvent(Map value) { // just expecting "payload" for now - return event.get("payload"); + if (logger.isDebugEnabled()) { + logger.info("Action Request Value:" + value); + } + if (value != null) { + Object payload = value.get("payload"); + return convertToFunctionParamType(payload); + } + return null; + } + + private Object convertToFunctionParamType(Object payload) { + try { + return mapper.convertValue(payload, getInputType()); + } catch (Exception e) { + throw new IllegalStateException("Cannot convert event payload", e); + } + } + + private String serializeBody(Object body) { + try { + return "{\"result\":" + mapper.writeValueAsString(body) + "}"; + } catch (JsonProcessingException e) { + throw new IllegalStateException("Cannot convert output", e); + } } } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/ActionRequest.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/OpenWhiskActionRequest.java similarity index 98% rename from spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/ActionRequest.java rename to spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/OpenWhiskActionRequest.java index 3ae57329b..55339c57b 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/ActionRequest.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/OpenWhiskActionRequest.java @@ -23,7 +23,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; /** * @author Mark Fisher */ -public class ActionRequest { +public class OpenWhiskActionRequest { @JsonProperty("action_name") private String actionName; diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/FunctionInitializer.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/OpenWhiskFunctionInitializer.java similarity index 85% rename from spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/FunctionInitializer.java rename to spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/OpenWhiskFunctionInitializer.java index f12f9caa2..0cff56986 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/FunctionInitializer.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/OpenWhiskFunctionInitializer.java @@ -21,19 +21,22 @@ import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.function.context.catalog.FunctionInspector; -import org.springframework.cloud.function.core.FluxFunction; import org.springframework.cloud.function.core.FunctionCatalog; -import org.springframework.cloud.function.core.FunctionFactoryUtils; import reactor.core.publisher.Flux; /** * @author Dave Syer * @author Mark Fisher + * @author Kamesh Sampath */ -public class FunctionInitializer { +public class OpenWhiskFunctionInitializer { + + private static Log logger = LogFactory.getLog(OpenWhiskFunctionInitializer.class); private Function, Flux> function; @@ -54,6 +57,7 @@ public class FunctionInitializer { @SuppressWarnings("unchecked") protected void initialize() { + logger.info("Initializing - OpenWhisk Function Initializer"); if (!this.initialized.compareAndSet(false, true)) { return; } @@ -61,15 +65,9 @@ public class FunctionInitializer { String type = this.properties.getType(); if ("function".equals(type)) { this.function = this.catalog.lookupFunction(name); - if (this.function != null && !FunctionFactoryUtils.isFluxFunction(this.function)) { - // TODO: this shouldn't be necessary - this.function = new FluxFunction(this.function); - } - } - else if ("consumer".equals(type)) { + } else if ("consumer".equals(type)) { this.consumer = this.catalog.lookupConsumer(name); - } - else if ("supplier".equals(type)) { + } else if ("supplier".equals(type)) { this.supplier = this.catalog.lookupSupplier(name); } } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/InitRequest.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/OpenWhiskInitRequest.java similarity index 96% rename from spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/InitRequest.java rename to spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/OpenWhiskInitRequest.java index 31f0af399..34a75a88d 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/InitRequest.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/main/java/org/springframework/cloud/function/adapter/openwhisk/OpenWhiskInitRequest.java @@ -19,7 +19,7 @@ package org.springframework.cloud.function.adapter.openwhisk; /** * @author Mark Fisher */ -public class InitRequest { +public class OpenWhiskInitRequest { private String name; diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/test/java/org/springframework/cloud/function/adapter/openwhisk/OpenWhiskActionHandlerTest.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/test/java/org/springframework/cloud/function/adapter/openwhisk/OpenWhiskActionHandlerTest.java new file mode 100644 index 000000000..35a2185ef --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/test/java/org/springframework/cloud/function/adapter/openwhisk/OpenWhiskActionHandlerTest.java @@ -0,0 +1,140 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.function.adapter.openwhisk; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Scope; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * @author Kamesh Sampath + */ +@RunWith(SpringRunner.class) +@SpringBootTest() +@EnableAutoConfiguration +@TestPropertySource(locations = "classpath:/application-test.properties") +public class OpenWhiskActionHandlerTest { + + @Autowired + OpenWhiskActionHandler actionHandler; + + @Autowired + ObjectMapper mapper; + + @Test + public void testHandlerWithPayload() { + Map testData = new HashMap<>(); + testData.put("name", "Spring"); + Map eventData = new HashMap<>(); + eventData.put("payload", testData); + actionHandler.init(new OpenWhiskInitRequest()); + OpenWhiskActionRequest actionRequest = new OpenWhiskActionRequest(); + actionRequest.setActionName("test_action"); + actionRequest.setValue(eventData); + Object result = actionHandler.run(actionRequest); + assertNotNull(result); + assertEquals("{\"result\":{\"name\":\"Spring\",\"message\":\"Hello, Spring\"}}", result); + } + + @Test + public void testHandlerWithoutPayload() { + Map testData = new HashMap<>(); + testData.put("name", "Spring"); + Map eventData = new HashMap<>(); + actionHandler.init(new OpenWhiskInitRequest()); + OpenWhiskActionRequest actionRequest = new OpenWhiskActionRequest(); + actionRequest.setActionName("test_action"); + Object result = actionHandler.run(actionRequest); + assertNotNull(result); + assertEquals("{\"result\":\"No input provided\"}", result); + } + + @Configuration + @Import({ContextFunctionCatalogAutoConfiguration.class, + JacksonAutoConfiguration.class}) + protected static class OWFunctionConfig { + + @Bean + public Function greeter() { + return v -> new OpenWhiskActionHandlerTest.Greetings(v.getName()); + } + + @Bean + @Scope("prototype") + public OpenWhiskActionHandler actionHandler() { + return new OpenWhiskActionHandler(); + } + + + @Bean + public FunctionProperties properties() { + return new FunctionProperties(); + } + + } + + protected static class Greetings { + + private final String GREETINGS_FORMAT = "Hello, %s"; + + private String name; + private String message; + + public Greetings() { + } + + public Greetings(String name) { + this.name = name; + setMessage(""); + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = String.format(GREETINGS_FORMAT, this.name != null ? name : "nobody"); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } + +} diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/test/resources/application-test.properties b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/test/resources/application-test.properties new file mode 100644 index 000000000..07b9a8b74 --- /dev/null +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/src/test/resources/application-test.properties @@ -0,0 +1,2 @@ +function.name: greeter +function.type: function \ No newline at end of file