Skip to content

Commit

Permalink
Register metadata about available functions with function-web
Browse files Browse the repository at this point in the history
  • Loading branch information
graemerocher committed Feb 26, 2018
1 parent c7fda24 commit 342a485
Show file tree
Hide file tree
Showing 11 changed files with 140 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.particleframework.context.annotation.Requires;
import org.particleframework.context.env.Environment;
import org.particleframework.core.convert.value.ConvertibleMultiValues;
import org.particleframework.core.convert.value.ConvertibleValues;
import org.particleframework.core.util.StringUtils;
import org.particleframework.discovery.ServiceInstance;
import org.particleframework.discovery.ServiceInstanceIdGenerator;
Expand All @@ -41,6 +42,7 @@
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
* Auto registration implementation for consul
Expand Down Expand Up @@ -184,6 +186,12 @@ protected void register(ServiceInstance instance) {
}
);

// include metadata as tags
ConvertibleValues<String> metadata = embeddedServerInstance.getMetadata();
for (Map.Entry<String, String> entry : metadata) {
tags.add(entry.getKey() + "=" + entry.getValue());
}

ConsulConfiguration.ConsulRegistrationConfiguration.CheckConfiguration checkConfig = registration.getCheck();
if (checkConfig.isEnabled()) {

Expand Down
11 changes: 10 additions & 1 deletion examples/petclinic/functions/tweet/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,28 @@ buildscript {
}
}

plugins {
id 'nebula.provided-base' version '3.0.3' // if you want provided-base
}

apply plugin:"groovy"
apply plugin: 'jp.classmethod.aws.lambda'
mainClassName = "example.function.tweet.UpdateStatusFunction"


dependencies {
compileOnly "org.particleframework:inject-java:${projectVersion}"
compile 'org.twitter4j:twitter4j-core:4.0.6'

compile "org.particleframework:inject-groovy:${projectVersion}"
compile "org.particleframework:function-groovy:${projectVersion}"


runtime "com.amazonaws:aws-lambda-java-log4j2:1.0.0"
runtime 'org.apache.logging.log4j:log4j-slf4j-impl:2.9.1'

provided "org.particleframework:discovery-client:${projectVersion}"
provided "org.particleframework:function-web:${projectVersion}"
provided "org.particleframework:http-server-netty:${projectVersion}"

testCompile "org.particleframework:http-client:${projectVersion}"
testRuntime "org.particleframework:function-web:${projectVersion}"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2018 original 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 example.function.tweet

import org.particleframework.runtime.ParticleApplication

/**
* @author graemerocher
* @since 1.0
*/
class Application {
static void main(String...args) {
ParticleApplication.run(Application, args)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
particle.application.name=tweet-function
consul.client.defaultZone=${CONSUL_HOST:localhost}:${CONSUL_PORT:8500}
comments.api.version=v1
2 changes: 1 addition & 1 deletion function-web/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ dependencies {
compile project(":router")
compile project(":http-server")

testCompile 'com.squareup.okhttp3:okhttp:3.8.1'
testCompile project(":http-client")
testCompile project(":inject-groovy")
testCompile project(":http-server-netty")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@

import org.particleframework.context.ExecutionHandleLocator;
import org.particleframework.context.annotation.Replaces;
import org.particleframework.context.annotation.Value;
import org.particleframework.context.processor.ExecutableMethodProcessor;
import org.particleframework.core.convert.ConversionService;
import org.particleframework.core.naming.NameUtils;
import org.particleframework.core.reflect.ClassUtils;
import org.particleframework.core.util.StringUtils;
import org.particleframework.discovery.ServiceInstance;
import org.particleframework.discovery.metadata.ServiceInstanceMetadataContributor;
import org.particleframework.function.DefaultLocalFunctionRegistry;
import org.particleframework.function.FunctionBean;
import org.particleframework.function.LocalFunctionRegistry;
Expand All @@ -32,7 +35,9 @@
import org.particleframework.web.router.UriRoute;

import javax.inject.Singleton;
import java.net.URI;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
Expand All @@ -47,50 +52,57 @@
*/
@Singleton
@Replaces(DefaultLocalFunctionRegistry.class)
public class AnnotatedFunctionRouteBuilder extends DefaultRouteBuilder implements ExecutableMethodProcessor<FunctionBean>, LocalFunctionRegistry {
public class AnnotatedFunctionRouteBuilder
extends DefaultRouteBuilder
implements ExecutableMethodProcessor<FunctionBean>, LocalFunctionRegistry, ServiceInstanceMetadataContributor {


private final LocalFunctionRegistry localFunctionRegistry;
private final String contextPath;
private final Map<String, URI> availableFunctions = new ConcurrentHashMap<>();

public AnnotatedFunctionRouteBuilder(
ExecutionHandleLocator executionHandleLocator,
UriNamingStrategy uriNamingStrategy,
ConversionService<?> conversionService) {
ConversionService<?> conversionService,
@Value("${function.contextPath:/}") String contextPath) {
super(executionHandleLocator, uriNamingStrategy, conversionService);
this.localFunctionRegistry = new DefaultLocalFunctionRegistry();
this.contextPath = contextPath.endsWith("/") ? contextPath : contextPath + '/';
}

@SuppressWarnings("unchecked")
@Override
public void process(BeanDefinition<?> beanDefinition, ExecutableMethod<?, ?> method) {
FunctionBean annotation = method.getAnnotation(FunctionBean.class);
if(annotation != null) {
String functionPath = annotation.value();
String functionName = annotation.value();
String functionPath = functionName;
Class<?> declaringType = method.getDeclaringType();
if(StringUtils.isEmpty(functionPath)) {
String typeName = declaringType.getSimpleName();
if(typeName.contains("$")) {
// generated lambda
functionPath = "/" + NameUtils.hyphenate(method.getMethodName());
functionPath = contextPath + NameUtils.hyphenate(method.getMethodName());
}
else {
functionPath = "/" + NameUtils.hyphenate(typeName);
functionPath = contextPath + NameUtils.hyphenate(typeName);
}
}
else {
functionPath = "/" + functionPath;
functionPath = contextPath + functionPath;
}

UriRoute route = null;
if(Stream.of(java.util.function.Function.class, Consumer.class, BiFunction.class, BiConsumer.class).anyMatch(type -> type.isAssignableFrom(declaringType))) {
route = POST(functionPath, method);

}
else if(Supplier.class.isAssignableFrom(declaringType)) {
route = GET(functionPath, method);
}

if(route != null) {
availableFunctions.put(functionName, URI.create(functionPath));
Class[] argumentTypes = method.getArgumentTypes();
int argCount = argumentTypes.length;
if(argCount > 0) {
Expand All @@ -107,6 +119,14 @@ else if(Supplier.class.isAssignableFrom(declaringType)) {
}
}

/**
* A map of available functions with the key being the function name and the value being the function URI
*
* @return A map of functions
*/
public Map<String, URI> getAvailableFunctions() {
return availableFunctions;
}

@Override
public <T, R> Optional<? extends ExecutableMethod<T, R>> findFirst() {
Expand Down Expand Up @@ -137,4 +157,12 @@ public <T, R> Optional<ExecutableMethod<java.util.function.Function<T, R>, R>> f
public <T, U, R> Optional<ExecutableMethod<BiFunction<T, U, R>, R>> findBiFunction(String name) {
return localFunctionRegistry.findBiFunction(name);
}

@Override
public void contribute(ServiceInstance instance, Map<String, String> metadata) {
for (Map.Entry<String, URI> entry : availableFunctions.entrySet()) {
String functionName = entry.getKey();
metadata.put(FUNCTION_PREFIX + functionName, entry.getValue().toString());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@
package org.particleframework.function.web

import groovy.transform.EqualsAndHashCode
import okhttp3.MediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
import org.particleframework.context.ApplicationContext
import org.particleframework.function.FunctionBean
import org.particleframework.function.LocalFunctionRegistry
import org.particleframework.http.HttpHeaders
import org.particleframework.http.HttpRequest
import org.particleframework.http.HttpResponse
import org.particleframework.http.HttpStatus
import org.particleframework.http.MediaType
import org.particleframework.http.client.RxHttpClient
import org.particleframework.runtime.server.EmbeddedServer
import spock.lang.Specification

Expand Down Expand Up @@ -53,17 +53,14 @@ class WebFunctionSpec extends Specification {
void "test string supplier"() {
given:
EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer)
String server = "http://localhost:$embeddedServer.port"
OkHttpClient client = new OkHttpClient()
def request = new Request.Builder()
.url("$server/supplier/string")
RxHttpClient client = embeddedServer.applicationContext.createBean(RxHttpClient, embeddedServer.getURL())

when:
def response = client.newCall(request.build()).execute()
HttpResponse<String> response = client.toBlocking().exchange('/supplier/string', String)

then:
response.code() == HttpStatus.OK.code
response.body().string() == 'value'
response.body() == 'value'

cleanup:
embeddedServer.stop()
Expand All @@ -72,17 +69,14 @@ class WebFunctionSpec extends Specification {
void "test pojo supplier"() {
given:
EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer)
String server = "http://localhost:$embeddedServer.port"
OkHttpClient client = new OkHttpClient()
def request = new Request.Builder()
.url("$server/supplier/pojo")
RxHttpClient client = embeddedServer.applicationContext.createBean(RxHttpClient, embeddedServer.getURL())

when:
def response = client.newCall(request.build()).execute()
HttpResponse<String> response = client.toBlocking().exchange('/supplier/pojo', String)

then:
response.code() == HttpStatus.OK.code
response.body().string() == '{"title":"The Stand"}'
response.body() == '{"title":"The Stand"}'
response.header(HttpHeaders.CONTENT_TYPE) == "application/json"


Expand All @@ -94,15 +88,11 @@ class WebFunctionSpec extends Specification {
void "test string consumer with JSON"() {
given:
EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer)
String server = "http://localhost:$embeddedServer.port"
OkHttpClient client = new OkHttpClient()
RxHttpClient client = embeddedServer.applicationContext.createBean(RxHttpClient, embeddedServer.getURL())
def data = '{"title":"The Stand"}'
def request = new Request.Builder()
.url("$server/consumer/string")
.post(RequestBody.create( MediaType.parse(org.particleframework.http.MediaType.APPLICATION_JSON), data))

when:
def response = client.newCall(request.build()).execute()
HttpResponse<?> response = client.toBlocking().exchange(HttpRequest.POST('/consumer/string', data))

then:
response.code() == HttpStatus.OK.code
Expand All @@ -115,15 +105,14 @@ class WebFunctionSpec extends Specification {
void "test string consumer with text plain"() {
given:
EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer)
String server = "http://localhost:$embeddedServer.port"
OkHttpClient client = new OkHttpClient()
RxHttpClient client = embeddedServer.applicationContext.createBean(RxHttpClient, embeddedServer.getURL())
def data = 'The Stand'
def request = new Request.Builder()
.url("$server/consumer/string")
.post(RequestBody.create( MediaType.parse("text/plain"), data))

when:
def response = client.newCall(request.build()).execute()
HttpResponse<?> response = client.toBlocking().exchange(
HttpRequest.POST('/consumer/string', data)
.contentType(MediaType.TEXT_PLAIN_TYPE)
)

then:
response.code() == HttpStatus.OK.code
Expand All @@ -136,13 +125,12 @@ class WebFunctionSpec extends Specification {
void "test pojo consumer"() {
given:
EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer)
String server = "http://localhost:$embeddedServer.port"
OkHttpClient client = new OkHttpClient()
def request = new Request.Builder()
.url("$server/consumer/pojo")
.post(RequestBody.create( MediaType.parse(org.particleframework.http.MediaType.APPLICATION_JSON), '{"title":"The Stand"}'))
RxHttpClient client = embeddedServer.applicationContext.createBean(RxHttpClient, embeddedServer.getURL())
def data = '{"title":"The Stand"}'

when:
def response = client.newCall(request.build()).execute()
HttpResponse<?> response = client.toBlocking().exchange(HttpRequest.POST('/consumer/pojo', data))


then:
response.code() == HttpStatus.OK.code
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ public interface LocalFunctionRegistry {
* The name of the default function name
*/
String FUNCTION_CHARSET = "particle.function.charset";
/**
* Prefix used to identify function names
*/
String FUNCTION_PREFIX = "function:";

/**
* Find the first available registered function
* @return The {@link ExecutableMethod} method representing the function
Expand Down
Loading

0 comments on commit 342a485

Please sign in to comment.