diff --git a/History.md b/History.md index 7d6a75c..0d71ce0 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,7 @@ +1.1.7 / 2018-04-11 +================== + + * Add http trigger support 1.1.6 / 2018-04-12 ================== diff --git a/pom.xml b/pom.xml index ffd5f18..e9cbb2b 100755 --- a/pom.xml +++ b/pom.xml @@ -1,173 +1,177 @@ - - 4.0.0 - com.aliyun - aliyun-java-sdk-fc - jar - 1.1.7-SNAPSHOT - aliyun-java-sdk-fc - https://www.aliyun.com/product/fc - Aliyun Java SDK for FunctionCompute + + 4.0.0 + com.aliyun + aliyun-java-sdk-fc + jar + 1.1.7-SNAPSHOT + aliyun-java-sdk-fc + https://www.aliyun.com/product/fc + Aliyun Java SDK for FunctionCompute + + + org.apache.httpcomponents + httpclient + 4.5.5 + + + junit + junit + 4.12 + test + + + com.aliyun + aliyun-java-sdk-sts + 2.1.6 + test + + + com.aliyun + aliyun-java-sdk-core + 2.1.7 + test + + + com.google.code.gson + gson + 2.2.4 + + + org.json + org.json + chargebee-1.0 + + + com.google.guava + guava + 19.0 + + + + + MIT + + + + + + + aliyunproducts + Aliyun SDK + aliyunsdk@aliyun.com + + - - - junit - junit - 4.12 - test - - - com.aliyun - aliyun-java-sdk-sts - 2.1.6 - test - - - com.aliyun - aliyun-java-sdk-core - 2.1.7 - test - - - com.google.code.gson - gson - 2.2.4 - - - org.json - org.json - chargebee-1.0 - - - com.google.guava - guava - 19.0 - - - - - MIT - - - - - - - aliyunproducts - Aliyun SDK - aliyunsdk@aliyun.com - - + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - + + scm:git:git://github.com/aliyun/fc-java-sdk.git + scm:git:ssh://github.com:aliyun/fc-java-sdk.git + http://github.com/aliyun/fc-java-sdk/tree/master + - - scm:git:git://github.com/aliyun/fc-java-sdk.git - scm:git:ssh://github.com:aliyun/fc-java-sdk.git - http://github.com/aliyun/fc-java-sdk/tree/master - + + + + src/main/resources + true + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.6 + 1.6 + UTF-8 + + + + org.apache.maven.plugins + maven-jar-plugin + 2.3.2 + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.8 + + UTF-8 + + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.10 + + -Dfile.encoding=UTF-8 + false + false + + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + attach-sources + + jar + + + + - - - - src/main/resources - true - - - - - org.apache.maven.plugins - maven-compiler-plugin - 2.3.2 - - 1.6 - 1.6 - UTF-8 - - - - org.apache.maven.plugins - maven-jar-plugin - 2.3.2 - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.8 - - UTF-8 - - - - attach-javadocs - - jar - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.10 - - -Dfile.encoding=UTF-8 - false - false - - - - org.apache.maven.plugins - maven-source-plugin - 3.0.1 - - - attach-sources - - jar - - - - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.5 - - - sign-artifacts - verify - - sign - - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.7 - true - - ossrh - https://oss.sonatype.org/ - true - - - - - + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.7 + true + + ossrh + https://oss.sonatype.org/ + true + + + + diff --git a/src/main/java/com/aliyuncs/fc/auth/FcSignatureComposer.java b/src/main/java/com/aliyuncs/fc/auth/FcSignatureComposer.java index 67dd10f..c48bfee 100644 --- a/src/main/java/com/aliyuncs/fc/auth/FcSignatureComposer.java +++ b/src/main/java/com/aliyuncs/fc/auth/FcSignatureComposer.java @@ -21,16 +21,18 @@ import java.io.UnsupportedEncodingException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; -import java.util.HashMap; -import java.util.Map; -import java.util.TreeMap; +import java.util.*; +import com.aliyuncs.fc.model.HttpMethod; import com.aliyuncs.fc.utils.Base64Helper; import com.aliyuncs.fc.utils.ParameterHelper; +import com.google.common.base.Joiner; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; +import static java.lang.String.format; + /** * TODO: add javadoc */ @@ -49,28 +51,98 @@ public static Map refreshSignParameters(Map para return parameters; } - public static String composeStringToSign(String method, String path, - Map headers) { + static String composeStringToSignWithMultiValue(HttpMethod method, String path, + Map headers, Map queries) { + StringBuilder sb = new StringBuilder(); + sb.append(method.name()); + sb.append(HEADER_SEPARATOR); + + sb.append(composeCanonicalizedFCHeaders(headers)); + sb.append(HEADER_SEPARATOR); + + sb.append(buildCanonicalHeaders(headers, "x-fc")); + + sb.append(composeCanonicalizedResource(path, queries)); + + return sb.toString(); + } + + private static String composeCanonicalizedFCHeaders(Map headers) { StringBuilder sb = new StringBuilder(); - sb.append(method).append(HEADER_SEPARATOR); - if (headers.get("Content-MD5") != null) { + + if (headers != null && headers.get("Content-MD5") != null) { sb.append(headers.get("Content-MD5")); } sb.append(HEADER_SEPARATOR); - if (headers.get("Content-Type") != null) { + if (headers != null && headers.get("Content-Type") != null) { sb.append(headers.get("Content-Type")); } + sb.append(HEADER_SEPARATOR); - if (headers.get("Date") != null) { + if (headers != null && headers.get("Date") != null) { sb.append(headers.get("Date")); } - sb.append(HEADER_SEPARATOR); - sb.append(buildCanonicalHeaders(headers, "x-fc")); + + return sb.toString(); + } + + private static String composeCanonicalizedResource(String path, Map queries) { + StringBuilder sb = new StringBuilder(); + sb.append(path); + + if (queries != null) { + sb.append(HEADER_SEPARATOR); + + List params = new ArrayList(queries.size()); + + for (Map.Entry query : queries.entrySet()) { + String key = query.getKey(); + String[] values = query.getValue(); + + if (values == null || values.length == 0) { + params.add(key); + continue; + } else { + for (String value : values) { + if (value == null) { + params.add(key); + } else { + params.add(format("%s=%s", key, value)); + } + } + } + } + + Collections.sort(params); + + sb.append(Joiner.on(HEADER_SEPARATOR) + .join(params)); + } + return sb.toString(); } + public static String composeStringToSign(HttpMethod method, String path, + Map headers, Map queries) { + + Map multiValueQueries = null; + + if (queries != null) { + multiValueQueries = new HashMap(); + + for (Map.Entry entry : queries.entrySet()) { + multiValueQueries.put(entry.getKey(), new String[] {entry.getValue()}); + } + } + + return composeStringToSignWithMultiValue(method, path, + headers, multiValueQueries); + } + public static String buildCanonicalHeaders(Map headers, String headerBegin) { + if (headers == null) return ""; + Map sortMap = new TreeMap(); for (Map.Entry e : headers.entrySet()) { String key = e.getKey().toLowerCase(); diff --git a/src/main/java/com/aliyuncs/fc/client/DefaultFcClient.java b/src/main/java/com/aliyuncs/fc/client/DefaultFcClient.java index 6ee65a5..b8ae44d 100644 --- a/src/main/java/com/aliyuncs/fc/client/DefaultFcClient.java +++ b/src/main/java/com/aliyuncs/fc/client/DefaultFcClient.java @@ -19,10 +19,22 @@ */ package com.aliyuncs.fc.client; +import com.aliyuncs.fc.auth.AcsURLEncoder; +import com.aliyuncs.fc.auth.FcSignatureComposer; +import com.aliyuncs.fc.config.Config; import com.aliyuncs.fc.constants.HeaderKeys; +import com.aliyuncs.fc.exceptions.ClientException; +import com.aliyuncs.fc.exceptions.ServerException; +import com.aliyuncs.fc.http.HttpRequest; +import com.aliyuncs.fc.http.HttpResponse; +import com.aliyuncs.fc.model.HttpMethod; +import com.aliyuncs.fc.model.PrepareUrl; +import com.aliyuncs.fc.request.HttpInvokeFunctionRequest; +import com.aliyuncs.fc.utils.ParameterHelper; import com.google.common.base.Preconditions; -import com.google.common.base.Strings; +import com.google.gson.Gson; import com.google.gson.JsonParseException; + import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.SocketTimeoutException; @@ -31,16 +43,9 @@ import java.util.HashMap; import java.util.Map; -import com.aliyuncs.fc.auth.FcSignatureComposer; -import com.aliyuncs.fc.config.Config; -import com.aliyuncs.fc.exceptions.ClientException; -import com.aliyuncs.fc.exceptions.ServerException; -import com.aliyuncs.fc.http.HttpRequest; -import com.aliyuncs.fc.http.HttpResponse; -import com.google.gson.Gson; -import com.aliyuncs.fc.auth.AcsURLEncoder; -import com.aliyuncs.fc.model.PrepareUrl; -import com.aliyuncs.fc.utils.ParameterHelper; +import static com.aliyuncs.fc.auth.FcSignatureComposer.composeStringToSign; +import static com.aliyuncs.fc.model.HttpAuthType.ANONYMOUS; +import static com.google.common.base.Strings.isNullOrEmpty; public class DefaultFcClient { public final static Boolean AUTO_RETRY = true; @@ -115,13 +120,13 @@ public Map getHeader(Map header, byte[] payload, if (payload != null) { header.put("Content-MD5", ParameterHelper.md5Sum(payload)); } - if (!Strings.isNullOrEmpty(config.getSecurityToken())) { + if (!isNullOrEmpty(config.getSecurityToken())) { header.put("x-fc-security-token", config.getSecurityToken()); } return header; } - public PrepareUrl signRequest(HttpRequest request, String form, String method) + public void signRequest(HttpRequest request, String form, HttpMethod method, boolean includeParameters) throws InvalidKeyException, IllegalStateException, UnsupportedEncodingException, NoSuchAlgorithmException { Map imutableMap = null; @@ -133,8 +138,8 @@ public PrepareUrl signRequest(HttpRequest request, String form, String method) String accessKeyId = config.getAccessKeyID(); String accessSecret = config.getAccessKeySecret(); - Preconditions.checkArgument(!Strings.isNullOrEmpty(accessKeyId), "Access key cannot be blank"); - Preconditions.checkArgument(!Strings.isNullOrEmpty(accessSecret), "Secret key cannot be blank"); + Preconditions.checkArgument(!isNullOrEmpty(accessKeyId), "Access key cannot be blank"); + Preconditions.checkArgument(!isNullOrEmpty(accessSecret), "Secret key cannot be blank"); imutableMap = FcSignatureComposer.refreshSignParameters(imutableMap); // Get relevant path @@ -144,33 +149,56 @@ public PrepareUrl signRequest(HttpRequest request, String form, String method) imutableMap = getHeader(imutableMap, request.getPayload(), form); // Sign URL - String strToSign = FcSignatureComposer.composeStringToSign(method, uri, imutableMap); + String strToSign = null; + + if (includeParameters) { + strToSign = composeStringToSign(method, uri, imutableMap, request.getQueryParams()); + } else { + strToSign = composeStringToSign(method, uri, imutableMap, null); + } + String signature = FcSignatureComposer.signString(strToSign, accessSecret); // Set signature imutableMap.put("Authorization", "FC " + accessKeyId + ":" + signature); - String allPath = composeUrl(config.getEndpoint() + request.getPath(), - request.getQueryParams()); - return new PrepareUrl(allPath); } - public HttpResponse doAction(HttpRequest request, String form, String method) + private PrepareUrl prepareUrl(String path, Map queryParams) throws UnsupportedEncodingException { + return new PrepareUrl(composeUrl(config.getEndpoint() + path, queryParams)); + } + + /** + * if form paramter is null, it will use content-type of request.headers + */ + public HttpResponse doAction(HttpRequest request, String form, HttpMethod method) throws ClientException, ServerException { request.validate(); try { - PrepareUrl prepareUrl = signRequest(request, form, method); - int retryTimes = 1; - HttpResponse response = HttpResponse.getResponse(prepareUrl.getUrl(), request, method, config.getConnectTimeoutMillis(), - config.getReadTimeoutMillis()); + int retryTimes = 0; + HttpResponse response = null; + + boolean httpInvoke = false; + if (request instanceof HttpInvokeFunctionRequest) httpInvoke = true; + + do { + if ( ! httpInvoke + || ! ANONYMOUS.equals(((HttpInvokeFunctionRequest) request).getAuthType())) { + signRequest(request, form, method, httpInvoke); + } + + PrepareUrl prepareUrl = prepareUrl(request.getPath(), request.getQueryParams()); - while (500 <= response.getStatus() && AUTO_RETRY && retryTimes < MAX_RETRIES) { - prepareUrl = signRequest(request, form, method); response = HttpResponse.getResponse(prepareUrl.getUrl(), request, method, - config.getConnectTimeoutMillis(), config.getReadTimeoutMillis()); + config.getConnectTimeoutMillis(), config.getReadTimeoutMillis()); + retryTimes++; - } + + if (httpInvoke) return response; + + } while (500 <= response.getStatus() && AUTO_RETRY && retryTimes < MAX_RETRIES); + if (response.getStatus() >= 500) { - String requestId = response.getHeaderValue(HeaderKeys.REQUEST_ID); + String requestId = response.getHeader(HeaderKeys.REQUEST_ID); String stringContent = response.getContent() == null ? "" : new String(response.getContent()); ServerException se; try { @@ -196,7 +224,7 @@ public HttpResponse doAction(HttpRequest request, String form, String method) ce = new ClientException("SDK.UnknownError", "Unknown client error"); } ce.setStatusCode(response.getStatus()); - ce.setRequestId(response.getHeaderValue(HeaderKeys.REQUEST_ID)); + ce.setRequestId(response.getHeader(HeaderKeys.REQUEST_ID)); throw ce; } return response; diff --git a/src/main/java/com/aliyuncs/fc/client/FunctionComputeClient.java b/src/main/java/com/aliyuncs/fc/client/FunctionComputeClient.java index 3c3d069..8797e67 100644 --- a/src/main/java/com/aliyuncs/fc/client/FunctionComputeClient.java +++ b/src/main/java/com/aliyuncs/fc/client/FunctionComputeClient.java @@ -1,24 +1,23 @@ package com.aliyuncs.fc.client; -import com.aliyuncs.fc.constants.HeaderKeys; -import com.aliyuncs.fc.request.*; import com.aliyuncs.fc.config.Config; +import com.aliyuncs.fc.constants.HeaderKeys; import com.aliyuncs.fc.exceptions.ClientException; import com.aliyuncs.fc.exceptions.ServerException; import com.aliyuncs.fc.http.HttpResponse; -import com.aliyuncs.fc.model.FunctionMetadata; -import com.aliyuncs.fc.model.FunctionCodeMetadata; -import com.aliyuncs.fc.model.ServiceMetadata; -import com.aliyuncs.fc.model.TriggerMetadata; +import com.aliyuncs.fc.model.*; +import com.aliyuncs.fc.request.*; import com.aliyuncs.fc.response.*; import com.aliyuncs.fc.utils.Base64Helper; -import com.google.common.base.Preconditions; import com.google.gson.Gson; + import java.io.IOException; import java.nio.charset.Charset; import java.util.Map; import java.util.logging.Logger; +import static com.aliyuncs.fc.model.HttpMethod.*; + /** * TODO: add javadoc */ @@ -57,7 +56,7 @@ public void setEndpoint(String endpoint) { public DeleteServiceResponse deleteService(DeleteServiceRequest request) throws ClientException, ServerException { - HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, "DELETE"); + HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, DELETE); DeleteServiceResponse deleteServiceResponse = new DeleteServiceResponse(); deleteServiceResponse.setHeaders(response.getHeaders()); deleteServiceResponse.setStatus(response.getStatus()); @@ -66,21 +65,21 @@ public DeleteServiceResponse deleteService(DeleteServiceRequest request) public DeleteFunctionResponse deleteFunction(DeleteFunctionRequest request) throws ClientException, ServerException { - HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, "DELETE"); + HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, DELETE); DeleteFunctionResponse deleteFunctionResponse = new DeleteFunctionResponse(); - deleteFunctionResponse.setHeader(response.getHeaders()); + deleteFunctionResponse.setHeaders(response.getHeaders()); deleteFunctionResponse.setStatus(response.getStatus()); return deleteFunctionResponse; } public GetServiceResponse getService(GetServiceRequest request) throws ClientException, ServerException { - HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, "GET"); + HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, GET); ServiceMetadata serviceMetadata = GSON.fromJson( new String(response.getContent()), ServiceMetadata.class); GetServiceResponse getServiceResponse = new GetServiceResponse(); getServiceResponse.setServiceMetadata(serviceMetadata); - getServiceResponse.setHeader(response.getHeaders()); + getServiceResponse.setHeaders(response.getHeaders()); getServiceResponse.setContent(response.getContent()); getServiceResponse.setStatus(response.getStatus()); return getServiceResponse; @@ -88,12 +87,12 @@ public GetServiceResponse getService(GetServiceRequest request) public GetFunctionResponse getFunction(GetFunctionRequest request) throws ClientException, ServerException { - HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, "GET"); + HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, GET); FunctionMetadata functionMetadata = GSON.fromJson( new String(response.getContent()), FunctionMetadata.class); GetFunctionResponse getFunctionResponse = new GetFunctionResponse(); getFunctionResponse.setFunctionMetadata(functionMetadata); - getFunctionResponse.setHeader(response.getHeaders()); + getFunctionResponse.setHeaders(response.getHeaders()); getFunctionResponse.setContent(response.getContent()); getFunctionResponse.setStatus(response.getStatus()); return getFunctionResponse; @@ -101,12 +100,12 @@ public GetFunctionResponse getFunction(GetFunctionRequest request) public GetFunctionCodeResponse getFunctionCode(GetFunctionCodeRequest request) throws ClientException, ServerException { - HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, "GET"); + HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, GET); FunctionCodeMetadata functionCodeMetadata = GSON.fromJson( new String(response.getContent()), FunctionCodeMetadata.class); GetFunctionCodeResponse getFunctionCodeResponse = new GetFunctionCodeResponse(); getFunctionCodeResponse.setFunctionCodeMetadata(functionCodeMetadata); - getFunctionCodeResponse.setHeader(response.getHeaders()); + getFunctionCodeResponse.setHeaders(response.getHeaders()); getFunctionCodeResponse.setContent(response.getContent()); getFunctionCodeResponse.setStatus(response.getStatus()); return getFunctionCodeResponse; @@ -114,7 +113,7 @@ public GetFunctionCodeResponse getFunctionCode(GetFunctionCodeRequest request) public CreateServiceResponse createService(CreateServiceRequest request) throws ClientException, ServerException { - HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, "POST"); + HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, POST); ServiceMetadata serviceMetadata = GSON.fromJson( new String(response.getContent()), ServiceMetadata.class); CreateServiceResponse createServiceResponse = new CreateServiceResponse(); @@ -127,12 +126,12 @@ public CreateServiceResponse createService(CreateServiceRequest request) public CreateFunctionResponse createFunction(CreateFunctionRequest request) throws ClientException, ServerException { - HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, "POST"); + HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, POST); FunctionMetadata functionMetadata = GSON.fromJson( new String(response.getContent()), FunctionMetadata.class); CreateFunctionResponse createFunctionResponse = new CreateFunctionResponse(); createFunctionResponse.setFunctionMetadata(functionMetadata); - createFunctionResponse.setHeader(response.getHeaders()); + createFunctionResponse.setHeaders(response.getHeaders()); createFunctionResponse.setContent(response.getContent()); createFunctionResponse.setStatus(response.getStatus()); return createFunctionResponse; @@ -140,12 +139,12 @@ public CreateFunctionResponse createFunction(CreateFunctionRequest request) public UpdateServiceResponse updateService(UpdateServiceRequest request) throws ClientException, ServerException { - HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, "PUT"); + HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, PUT); ServiceMetadata serviceMetadata = GSON.fromJson( new String(response.getContent()), ServiceMetadata.class); UpdateServiceResponse updateServiceResponse = new UpdateServiceResponse(); updateServiceResponse.setServiceMetadata(serviceMetadata); - updateServiceResponse.setHeader(response.getHeaders()); + updateServiceResponse.setHeaders(response.getHeaders()); updateServiceResponse.setContent(response.getContent()); updateServiceResponse.setStatus(response.getStatus()); return updateServiceResponse; @@ -153,12 +152,12 @@ public UpdateServiceResponse updateService(UpdateServiceRequest request) public UpdateFunctionResponse updateFunction(UpdateFunctionRequest request) throws ClientException, ServerException { - HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, "PUT"); + HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, PUT); FunctionMetadata functionMetadata = GSON.fromJson( new String(response.getContent()), FunctionMetadata.class); UpdateFunctionResponse updateFunctionResponse = new UpdateFunctionResponse(); updateFunctionResponse.setFunctionMetadata(functionMetadata); - updateFunctionResponse.setHeader(response.getHeaders()); + updateFunctionResponse.setHeaders(response.getHeaders()); updateFunctionResponse.setContent(response.getContent()); updateFunctionResponse.setStatus(response.getStatus()); return updateFunctionResponse; @@ -166,10 +165,10 @@ public UpdateFunctionResponse updateFunction(UpdateFunctionRequest request) public ListServicesResponse listServices(ListServicesRequest request) throws ClientException, ServerException { - HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, "GET"); + HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, GET); ListServicesResponse listServicesResponse = GSON.fromJson( new String(response.getContent()), ListServicesResponse.class); - listServicesResponse.setHeader(response.getHeaders()); + listServicesResponse.setHeaders(response.getHeaders()); listServicesResponse.setContent(response.getContent()); listServicesResponse.setStatus(response.getStatus()); return listServicesResponse; @@ -177,10 +176,10 @@ public ListServicesResponse listServices(ListServicesRequest request) public ListFunctionsResponse listFunctions(ListFunctionsRequest request) throws ClientException, ServerException { - HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, "GET"); + HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, GET); ListFunctionsResponse listFunctionsResponse = GSON.fromJson( new String(response.getContent()), ListFunctionsResponse.class); - listFunctionsResponse.setHeader(response.getHeaders()); + listFunctionsResponse.setHeaders(response.getHeaders()); listFunctionsResponse.setContent(response.getContent()); listFunctionsResponse.setStatus(response.getStatus()); return listFunctionsResponse; @@ -188,12 +187,12 @@ public ListFunctionsResponse listFunctions(ListFunctionsRequest request) public CreateTriggerResponse createTrigger(CreateTriggerRequest request) throws ClientException, ServerException { - HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, "POST"); + HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, POST); TriggerMetadata triggerMetadata = GSON.fromJson( new String(response.getContent()), TriggerMetadata.class); CreateTriggerResponse createTriggerResponse = new CreateTriggerResponse(); createTriggerResponse.setTriggerMetadata(triggerMetadata); - createTriggerResponse.setHeader(response.getHeaders()); + createTriggerResponse.setHeaders(response.getHeaders()); createTriggerResponse.setContent(response.getContent()); createTriggerResponse.setStatus(response.getStatus()); return createTriggerResponse; @@ -201,21 +200,21 @@ public CreateTriggerResponse createTrigger(CreateTriggerRequest request) public DeleteTriggerResponse deleteTrigger(DeleteTriggerRequest request) throws ClientException, ServerException { - HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, "DELETE"); + HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, DELETE); DeleteTriggerResponse deleteTriggerResponse = new DeleteTriggerResponse(); - deleteTriggerResponse.setHeader(response.getHeaders()); + deleteTriggerResponse.setHeaders(response.getHeaders()); deleteTriggerResponse.setStatus(response.getStatus()); return deleteTriggerResponse; } public UpdateTriggerResponse updateTrigger(UpdateTriggerRequest request) throws ClientException, ServerException { - HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, "PUT"); + HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, PUT); TriggerMetadata triggerMetadata = GSON.fromJson( new String(response.getContent()), TriggerMetadata.class); UpdateTriggerResponse updateTriggerResponse = new UpdateTriggerResponse(); updateTriggerResponse.setTriggerMetadata(triggerMetadata); - updateTriggerResponse.setHeader(response.getHeaders()); + updateTriggerResponse.setHeaders(response.getHeaders()); updateTriggerResponse.setContent(response.getContent()); updateTriggerResponse.setStatus(response.getStatus()); return updateTriggerResponse; @@ -223,12 +222,12 @@ public UpdateTriggerResponse updateTrigger(UpdateTriggerRequest request) public GetTriggerResponse getTrigger(GetTriggerRequest request) throws ClientException, ServerException { - HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, "GET"); + HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, GET); TriggerMetadata triggerMetadata = GSON.fromJson( new String(response.getContent()), TriggerMetadata.class); GetTriggerResponse getTriggerResponse = new GetTriggerResponse(); getTriggerResponse.setTriggerMetadata(triggerMetadata); - getTriggerResponse.setHeader(response.getHeaders()); + getTriggerResponse.setHeaders(response.getHeaders()); getTriggerResponse.setContent(response.getContent()); getTriggerResponse.setStatus(response.getStatus()); return getTriggerResponse; @@ -236,10 +235,10 @@ public GetTriggerResponse getTrigger(GetTriggerRequest request) public ListTriggersResponse listTriggers(ListTriggersRequest request) throws ClientException, ServerException { - HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, "GET"); + HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_JSON, GET); ListTriggersResponse listTriggersResponse = GSON.fromJson( new String(response.getContent()), ListTriggersResponse.class); - listTriggersResponse.setHeader(response.getHeaders()); + listTriggersResponse.setHeaders(response.getHeaders()); listTriggersResponse.setContent(response.getContent()); listTriggersResponse.setStatus(response.getStatus()); return listTriggersResponse; @@ -247,12 +246,30 @@ public ListTriggersResponse listTriggers(ListTriggersRequest request) public InvokeFunctionResponse invokeFunction(InvokeFunctionRequest request) throws ClientException, ServerException { - HttpResponse response = client.doAction(request, CONTENT_TYPE_APPLICATION_STREAM, "POST"); + + HttpResponse response = null; + + if (request instanceof HttpInvokeFunctionRequest) { + String form = null; + + if (request.getHeaders() != null && request.getHeaders().containsKey("Content-Type")) { + form = request.getHeaders().get("Content-Type"); + } + + if (form == null) { + form = CONTENT_TYPE_APPLICATION_STREAM; + } + + response = client.doAction(request, form, ((HttpInvokeFunctionRequest) request).getMethod()); + } else { + response = client.doAction(request, CONTENT_TYPE_APPLICATION_STREAM, POST); + } + InvokeFunctionResponse invokeFunctionResponse = new InvokeFunctionResponse(); invokeFunctionResponse.setContent(response.getContent()); invokeFunctionResponse.setPayload(response.getContent()); - invokeFunctionResponse.setHeader(response.getHeaders()); + invokeFunctionResponse.setHeaders(response.getHeaders()); invokeFunctionResponse.setStatus(response.getStatus()); Map headers = response.getHeaders(); if (headers != null && headers.containsKey(HeaderKeys.INVOCATION_LOG_RESULT)) { diff --git a/src/main/java/com/aliyuncs/fc/constants/Const.java b/src/main/java/com/aliyuncs/fc/constants/Const.java index 2f5097e..f784153 100644 --- a/src/main/java/com/aliyuncs/fc/constants/Const.java +++ b/src/main/java/com/aliyuncs/fc/constants/Const.java @@ -13,8 +13,10 @@ public class Const { public final static String TRIGGER_PATH = "/%s/services/%s/functions/%s/triggers"; public final static String SINGLE_TRIGGER_PATH = "/%s/services/%s/functions/%s/triggers/%s"; public final static String INVOKE_FUNCTION_PATH = "/%s/services/%s/functions/%s/invocations"; + public final static String HTTP_INVOKE_FUNCTION_PATH = "/%s/proxy/%s/%s/%s"; public final static int CONNECT_TIMEOUT = 1000; public final static int READ_TIMEOUT = 100000; public final static String API_VERSION = "2016-08-15"; public final static String INVOCATION_TYPE_ASYNC = "Async"; + public final static String INVOCATION_TYPE_HTTP = "http"; } diff --git a/src/main/java/com/aliyuncs/fc/http/HttpRequest.java b/src/main/java/com/aliyuncs/fc/http/HttpRequest.java index 895b088..e3361c9 100644 --- a/src/main/java/com/aliyuncs/fc/http/HttpRequest.java +++ b/src/main/java/com/aliyuncs/fc/http/HttpRequest.java @@ -41,20 +41,14 @@ public HttpRequest() { headers = new HashMap(); } - public HttpURLConnection getHttpConnection(String urls, byte[] content, String method) + public HttpURLConnection getHttpConnection(String urls, String method) throws IOException { String strUrl = urls; if (null == strUrl || null == method) { return null; } - URL url = null; - String[] urlArray = null; - if ("POST".equals(method) && null == content) { - urlArray = strUrl.split("\\?"); - url = new URL(urlArray[0]); - } else { - url = new URL(strUrl); - } + URL url = new URL(strUrl); + System.setProperty("sun.net.http.allowRestrictedHeaders", "true"); HttpURLConnection httpConn = (HttpURLConnection) url.openConnection(); httpConn.setRequestMethod(method); @@ -68,9 +62,6 @@ public HttpURLConnection getHttpConnection(String urls, byte[] content, String m httpConn.setRequestProperty(entry.getKey(), entry.getValue()); } - if ("POST".equals(method) && null != urlArray && urlArray.length == 2) { - httpConn.getOutputStream().write(urlArray[1].getBytes()); - } return httpConn; } diff --git a/src/main/java/com/aliyuncs/fc/http/HttpResponse.java b/src/main/java/com/aliyuncs/fc/http/HttpResponse.java index b2dc97a..61d3b81 100644 --- a/src/main/java/com/aliyuncs/fc/http/HttpResponse.java +++ b/src/main/java/com/aliyuncs/fc/http/HttpResponse.java @@ -18,6 +18,8 @@ */ package com.aliyuncs.fc.http; +import com.aliyuncs.fc.model.HttpMethod; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -37,13 +39,17 @@ public class HttpResponse { public HttpResponse() { } - public void putHeaderParameter(String key, String value) { + public void setHeader(String key, String value) { if (headers == null) { headers = new HashMap(); } headers.put(key, value); } + public void setHeaders(Map headers) { + this.headers = headers; + } + public void setContent(byte[] content) { this.content = content; } @@ -52,8 +58,7 @@ public byte[] getContent() { return this.content; } - - public String getHeaderValue(String name) { + public String getHeader(String name) { String value = this.headers.get(name); if (null == value) { value = this.headers.get(name.toLowerCase()); @@ -101,7 +106,7 @@ private static void parseHttpConn(HttpResponse response, HttpURLConnection httpC builder.append(","); builder.append(values.get(i)); } - response.putHeaderParameter(key, builder.toString()); + response.setHeader(key, builder.toString()); } response.setContent(buff); @@ -109,12 +114,12 @@ private static void parseHttpConn(HttpResponse response, HttpURLConnection httpC // Get http response public static HttpResponse getResponse(String urls, HttpRequest request, - String method, int connectTimeoutMillis, int readTimeoutMillis) throws IOException { + HttpMethod method, int connectTimeoutMillis, int readTimeoutMillis) throws IOException { OutputStream out = null; InputStream content = null; HttpResponse response = null; HttpURLConnection httpConn = request - .getHttpConnection(urls, request.getPayload(), method); + .getHttpConnection(urls, method.name()); httpConn.setConnectTimeout(connectTimeoutMillis); httpConn.setReadTimeout(readTimeoutMillis); @@ -159,4 +164,11 @@ public boolean isSuccess() { return false; } + public String getRequestId() { + return this.headers.get("X-Fc-Request-Id"); + } + + public String getEtag() { + return this.headers.get("Etag"); + } } diff --git a/src/main/java/com/aliyuncs/fc/model/HttpAuthType.java b/src/main/java/com/aliyuncs/fc/model/HttpAuthType.java new file mode 100644 index 0000000..82db5a6 --- /dev/null +++ b/src/main/java/com/aliyuncs/fc/model/HttpAuthType.java @@ -0,0 +1,20 @@ +package com.aliyuncs.fc.model; + +import com.google.gson.annotations.SerializedName; + +public enum HttpAuthType { + + /** + * Invoke an FC function without authorization. + */ + @SerializedName("anonymous") + ANONYMOUS, + + /** + * Invoke an FC function via authorization with Aliyun AK signed. + * + * link https://help.aliyun.com/document_detail/53252.html + */ + @SerializedName("function") + FUNCTION +} diff --git a/src/main/java/com/aliyuncs/fc/model/HttpMethod.java b/src/main/java/com/aliyuncs/fc/model/HttpMethod.java new file mode 100644 index 0000000..e5872f0 --- /dev/null +++ b/src/main/java/com/aliyuncs/fc/model/HttpMethod.java @@ -0,0 +1,5 @@ +package com.aliyuncs.fc.model; + +public enum HttpMethod { + GET, POST, PUT, HEAD, DELETE +} diff --git a/src/main/java/com/aliyuncs/fc/model/HttpTriggerConfig.java b/src/main/java/com/aliyuncs/fc/model/HttpTriggerConfig.java new file mode 100644 index 0000000..2ea0040 --- /dev/null +++ b/src/main/java/com/aliyuncs/fc/model/HttpTriggerConfig.java @@ -0,0 +1,25 @@ +package com.aliyuncs.fc.model; + +import com.google.gson.annotations.SerializedName; + +public class HttpTriggerConfig { + + @SerializedName("authType") + private final HttpAuthType authType; + + @SerializedName("methods") + private final HttpMethod[] methods; + + public HttpTriggerConfig(HttpAuthType authType, HttpMethod[] methods) { + this.authType = authType; + this.methods = methods; + } + + public HttpAuthType getAuthType() { + return authType; + } + + public HttpMethod[] getMethods() { + return methods; + } +} diff --git a/src/main/java/com/aliyuncs/fc/request/HttpInvokeFunctionRequest.java b/src/main/java/com/aliyuncs/fc/request/HttpInvokeFunctionRequest.java new file mode 100644 index 0000000..7addf73 --- /dev/null +++ b/src/main/java/com/aliyuncs/fc/request/HttpInvokeFunctionRequest.java @@ -0,0 +1,80 @@ +package com.aliyuncs.fc.request; + +import com.aliyuncs.fc.constants.Const; +import com.aliyuncs.fc.model.HttpAuthType; +import com.aliyuncs.fc.model.HttpMethod; +import org.apache.http.NameValuePair; +import org.apache.http.client.utils.URIBuilder; + +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.Map; + +import static java.lang.String.format; + +/** + * used for http invocation + */ +public class HttpInvokeFunctionRequest extends InvokeFunctionRequest { + + private final String path; + + private final HttpMethod method; + + private final HttpAuthType authType; + + private final Map queryParams = new HashMap(); + + public HttpInvokeFunctionRequest(String serviceName, String functionName, HttpAuthType authType, HttpMethod method) { + this(serviceName, functionName, authType, method, ""); + } + + public HttpInvokeFunctionRequest(String serviceName, String functionName, HttpAuthType authType, HttpMethod method, String path) { + super(serviceName, functionName); + + this.setInvocationType(Const.INVOCATION_TYPE_HTTP); + + try { + // parse path, path may contain parameters + if (path != null) { + URIBuilder uriBuilder = new URIBuilder(path); + path = uriBuilder.getPath(); + + for (NameValuePair pair : uriBuilder.getQueryParams()) { + addQuery(pair.getName(), pair.getValue()); + } + } + + this.path = path == null ? "" : (path.startsWith("/") ? path.substring(1) : path); + + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e.getMessage(), e); + } + + this.authType = authType; + this.method = method; + } + + public void addQuery(String name, String value) { + if (value == null) value = ""; + this.queryParams.put(name, value); + } + + @Override + public Map getQueryParams() { + return queryParams; + } + + @Override + public String getPath() { + return format(Const.HTTP_INVOKE_FUNCTION_PATH, Const.API_VERSION, getServiceName(), getFunctionName(), path); + } + + public HttpMethod getMethod() { + return method; + } + + public HttpAuthType getAuthType() { + return authType; + } +} diff --git a/src/main/java/com/aliyuncs/fc/request/InvokeFunctionRequest.java b/src/main/java/com/aliyuncs/fc/request/InvokeFunctionRequest.java index fe3ba94..4ad2fee 100755 --- a/src/main/java/com/aliyuncs/fc/request/InvokeFunctionRequest.java +++ b/src/main/java/com/aliyuncs/fc/request/InvokeFunctionRequest.java @@ -27,6 +27,10 @@ import com.google.common.base.Strings; import java.util.Map; +import static com.aliyuncs.fc.constants.Const.INVOCATION_TYPE_HTTP; +import static com.google.common.base.Strings.isNullOrEmpty; +import static java.lang.String.format; + /** * TODO: add javadoc */ @@ -36,6 +40,7 @@ public class InvokeFunctionRequest extends HttpRequest { private final String functionName; private String invocationType; private String logType; + private byte[] payload; public InvokeFunctionRequest(String serviceName, String functionName) { @@ -79,16 +84,15 @@ public Map getQueryParams() { } public String getPath() { - return String.format(Const.INVOKE_FUNCTION_PATH, Const.API_VERSION, serviceName, - functionName); + return format(Const.INVOKE_FUNCTION_PATH, Const.API_VERSION, serviceName, functionName); } public Map getHeaders() { - if (!Strings.isNullOrEmpty(invocationType)) { + if (!isNullOrEmpty(invocationType) && !Const.INVOCATION_TYPE_HTTP.equalsIgnoreCase(invocationType)) { headers.put(HeaderKeys.INVOCATION_TYPE, invocationType); } - if (!Strings.isNullOrEmpty(logType)) { + if (!isNullOrEmpty(logType)) { headers.put(HeaderKeys.INVOCATION_LOG_TYPE, logType); } @@ -96,10 +100,10 @@ public Map getHeaders() { } public void validate() throws ClientException { - if (Strings.isNullOrEmpty(serviceName)) { + if (isNullOrEmpty(serviceName)) { throw new ClientException("Service name cannot be blank"); } - if (Strings.isNullOrEmpty(functionName)) { + if (isNullOrEmpty(functionName)) { throw new ClientException("Function name cannot be blank"); } } @@ -114,5 +118,4 @@ public byte[] getPayload() { public Class getResponseClass() { return InvokeFunctionResponse.class; } - } diff --git a/src/main/java/com/aliyuncs/fc/response/CreateFunctionResponse.java b/src/main/java/com/aliyuncs/fc/response/CreateFunctionResponse.java index 925c0a2..11ceee3 100755 --- a/src/main/java/com/aliyuncs/fc/response/CreateFunctionResponse.java +++ b/src/main/java/com/aliyuncs/fc/response/CreateFunctionResponse.java @@ -29,7 +29,6 @@ */ public class CreateFunctionResponse extends HttpResponse { - private Map header; private FunctionMetadata functionMetadata; public CreateFunctionResponse setFunctionMetadata(FunctionMetadata functionMetadata) { @@ -96,23 +95,4 @@ public Map getEnvironmentVariables() { Preconditions.checkArgument(functionMetadata != null); return functionMetadata.getEnvironmentVariables(); } - - public CreateFunctionResponse setHeader(Map header) { - this.header = header; - return this; - } - - public Map getHeader() { - return header; - } - - public String getRequestId() { - return header.get("X-Fc-Request-Id"); - } - - public String getEtag() { - return header.get("Etag"); - } - - } diff --git a/src/main/java/com/aliyuncs/fc/response/CreateServiceResponse.java b/src/main/java/com/aliyuncs/fc/response/CreateServiceResponse.java index 798b624..9686a53 100755 --- a/src/main/java/com/aliyuncs/fc/response/CreateServiceResponse.java +++ b/src/main/java/com/aliyuncs/fc/response/CreateServiceResponse.java @@ -29,15 +29,8 @@ * TODO: add javadoc */ public class CreateServiceResponse extends HttpResponse { - - private Map header; private ServiceMetadata serviceMetadata; - public CreateServiceResponse setHeaders(Map header) { - this.header = header; - return this; - } - public CreateServiceResponse setServiceMetadata(ServiceMetadata serviceMetadata) { this.serviceMetadata = serviceMetadata; return this; @@ -76,17 +69,4 @@ public String getLastModifiedTime() { Preconditions.checkArgument(serviceMetadata != null); return serviceMetadata.getLastModifiedTime(); } - - public Map getHeader() { - return this.header; - } - - public String getRequestId() { - return header.get("X-Fc-Request-Id"); - } - - public String getEtag() { - return header.get("Etag"); - } - } diff --git a/src/main/java/com/aliyuncs/fc/response/CreateTriggerResponse.java b/src/main/java/com/aliyuncs/fc/response/CreateTriggerResponse.java index 178a107..4099dd2 100755 --- a/src/main/java/com/aliyuncs/fc/response/CreateTriggerResponse.java +++ b/src/main/java/com/aliyuncs/fc/response/CreateTriggerResponse.java @@ -18,11 +18,10 @@ */ package com.aliyuncs.fc.response; -import com.aliyuncs.fc.model.OSSTriggerConfig; -import com.aliyuncs.fc.model.TriggerMetadata; import com.aliyuncs.fc.http.HttpResponse; - +import com.aliyuncs.fc.model.TriggerMetadata; import com.google.common.base.Preconditions; + import java.util.Map; /** @@ -30,23 +29,13 @@ */ public class CreateTriggerResponse extends HttpResponse { - private Map header; private TriggerMetadata triggerMetadata; - public CreateTriggerResponse setHeader(Map header) { - this.header = header; - return this; - } - public CreateTriggerResponse setTriggerMetadata(TriggerMetadata triggerMetadata) { this.triggerMetadata = triggerMetadata; return this; } - public Map getHeader() { - return header; - } - public String getTriggerName() { Preconditions.checkArgument(triggerMetadata != null); return triggerMetadata.getTriggerName(); @@ -75,13 +64,4 @@ public Object getTriggerConfig() { Preconditions.checkArgument(triggerMetadata != null); return triggerMetadata.getTriggerConfig(); } - - public String getRequestId() { - return header.get("X-Fc-Request-Id"); - } - - public String getEtag() { - return header.get("Etag"); - } - } diff --git a/src/main/java/com/aliyuncs/fc/response/DeleteFunctionResponse.java b/src/main/java/com/aliyuncs/fc/response/DeleteFunctionResponse.java index d3e0aab..25c47b0 100755 --- a/src/main/java/com/aliyuncs/fc/response/DeleteFunctionResponse.java +++ b/src/main/java/com/aliyuncs/fc/response/DeleteFunctionResponse.java @@ -26,23 +26,4 @@ */ public class DeleteFunctionResponse extends HttpResponse { - private Map header = null; - - public Map getHeader() { - return header; - } - - public DeleteFunctionResponse setHeader(Map header) { - this.header = header; - return this; - } - - public String getRequestId() { - return header.get("X-Fc-Request-Id"); - } - - public String getEtag() { - return header.get("Etag"); - } - } diff --git a/src/main/java/com/aliyuncs/fc/response/DeleteServiceResponse.java b/src/main/java/com/aliyuncs/fc/response/DeleteServiceResponse.java index 3b783a7..238c764 100755 --- a/src/main/java/com/aliyuncs/fc/response/DeleteServiceResponse.java +++ b/src/main/java/com/aliyuncs/fc/response/DeleteServiceResponse.java @@ -26,23 +26,4 @@ */ public class DeleteServiceResponse extends HttpResponse { - private Map header = null; - - public Map getHeader() { - return header; - } - - public DeleteServiceResponse setHeaders(Map header) { - this.header = header; - return this; - } - - public String getRequestId() { - return header.get("X-Fc-Request-Id"); - } - - public String getEtag() { - return header.get("Etag"); - } - } diff --git a/src/main/java/com/aliyuncs/fc/response/DeleteTriggerResponse.java b/src/main/java/com/aliyuncs/fc/response/DeleteTriggerResponse.java index 252616b..51e1a15 100755 --- a/src/main/java/com/aliyuncs/fc/response/DeleteTriggerResponse.java +++ b/src/main/java/com/aliyuncs/fc/response/DeleteTriggerResponse.java @@ -27,23 +27,4 @@ */ public class DeleteTriggerResponse extends HttpResponse { - private Map header = null; - - public Map getHeader() { - return header; - } - - public DeleteTriggerResponse setHeader(Map header) { - this.header = header; - return this; - } - - public String getRequestId() { - return header.get("X-Fc-Request-Id"); - } - - public String getEtag() { - return header.get("Etag"); - } - } diff --git a/src/main/java/com/aliyuncs/fc/response/GetFunctionCodeResponse.java b/src/main/java/com/aliyuncs/fc/response/GetFunctionCodeResponse.java index b4cb27a..19b08be 100644 --- a/src/main/java/com/aliyuncs/fc/response/GetFunctionCodeResponse.java +++ b/src/main/java/com/aliyuncs/fc/response/GetFunctionCodeResponse.java @@ -11,7 +11,6 @@ */ public class GetFunctionCodeResponse extends HttpResponse { - private Map header; private FunctionCodeMetadata functionCodeMetadata; public void setFunctionCodeMetadata(FunctionCodeMetadata functionCodeMetadata) { @@ -32,16 +31,4 @@ public String getCodeChecksum() { Preconditions.checkArgument(functionCodeMetadata != null); return functionCodeMetadata.getChecksum(); } - - public Map getHeader() { - return header; - } - - public void setHeader(Map header) { - this.header = header; - } - - public String getRequestId() { - return header.get("X-Fc-Request-Id"); - } } diff --git a/src/main/java/com/aliyuncs/fc/response/GetFunctionResponse.java b/src/main/java/com/aliyuncs/fc/response/GetFunctionResponse.java index d2484de..15e69e7 100755 --- a/src/main/java/com/aliyuncs/fc/response/GetFunctionResponse.java +++ b/src/main/java/com/aliyuncs/fc/response/GetFunctionResponse.java @@ -29,7 +29,6 @@ */ public class GetFunctionResponse extends HttpResponse { - private Map header; private FunctionMetadata functionMetadata; public void setFunctionMetadata(FunctionMetadata functionMetadata) { @@ -100,21 +99,4 @@ public Map getEnvironmentVariables() { Preconditions.checkArgument(functionMetadata != null); return functionMetadata.getEnvironmentVariables(); } - - public Map getHeader() { - return header; - } - - public void setHeader(Map header) { - this.header = header; - } - - public String getRequestId() { - return header.get("X-Fc-Request-Id"); - } - - public String getEtag() { - return header.get("Etag"); - } - } diff --git a/src/main/java/com/aliyuncs/fc/response/GetServiceResponse.java b/src/main/java/com/aliyuncs/fc/response/GetServiceResponse.java index b7ace02..61f808c 100755 --- a/src/main/java/com/aliyuncs/fc/response/GetServiceResponse.java +++ b/src/main/java/com/aliyuncs/fc/response/GetServiceResponse.java @@ -30,18 +30,8 @@ */ public class GetServiceResponse extends HttpResponse { - private Map header; private ServiceMetadata serviceMetadata; - public Map getHeader() { - return header; - } - - public GetServiceResponse setHeader(Map header) { - this.header = header; - return this; - } - public String getServiceName() { Preconditions.checkArgument(serviceMetadata != null); return serviceMetadata.getServiceName(); @@ -81,13 +71,4 @@ public GetServiceResponse setServiceMetadata(ServiceMetadata serviceMetadata) { this.serviceMetadata = serviceMetadata; return this; } - - public String getRequestId() { - return header.get("X-Fc-Request-Id"); - } - - public String getEtag() { - return header.get("Etag"); - } - } diff --git a/src/main/java/com/aliyuncs/fc/response/GetTriggerResponse.java b/src/main/java/com/aliyuncs/fc/response/GetTriggerResponse.java index db7f635..5c918be 100755 --- a/src/main/java/com/aliyuncs/fc/response/GetTriggerResponse.java +++ b/src/main/java/com/aliyuncs/fc/response/GetTriggerResponse.java @@ -30,18 +30,8 @@ */ public class GetTriggerResponse extends HttpResponse { - private Map header; private TriggerMetadata triggerMetadata; - public Map getHeader() { - return header; - } - - public GetTriggerResponse setHeader(Map header) { - this.header = header; - return this; - } - public GetTriggerResponse setTriggerMetadata(TriggerMetadata triggerMetadata) { this.triggerMetadata = triggerMetadata; return this; @@ -82,12 +72,4 @@ public Object getTriggerConfig() { return triggerMetadata.getTriggerConfig(); } - public String getRequestId() { - return header.get("X-Fc-Request-Id"); - } - - public String getEtag() { - return header.get("Etag"); - } - } diff --git a/src/main/java/com/aliyuncs/fc/response/InvokeFunctionResponse.java b/src/main/java/com/aliyuncs/fc/response/InvokeFunctionResponse.java index 7795679..848debf 100755 --- a/src/main/java/com/aliyuncs/fc/response/InvokeFunctionResponse.java +++ b/src/main/java/com/aliyuncs/fc/response/InvokeFunctionResponse.java @@ -20,26 +20,14 @@ import com.aliyuncs.fc.http.HttpResponse; -import java.util.Map; - /** * TODO: add javadoc */ public class InvokeFunctionResponse extends HttpResponse { - private Map header; private String logResult; private byte[] payload; - public Map getHeader() { - return header; - } - - public InvokeFunctionResponse setHeader(Map header) { - this.header = header; - return this; - } - public InvokeFunctionResponse setLogResult(String logResult) { this.logResult = logResult; return this; @@ -54,14 +42,6 @@ public InvokeFunctionResponse setPayload(byte[] payload) { return this; } - public String getRequestId() { - return header.get("X-Fc-Request-Id"); - } - - public String getEtag() { - return header.get("Etag"); - } - public String getLogResult() { return logResult; } diff --git a/src/main/java/com/aliyuncs/fc/response/ListFunctionsResponse.java b/src/main/java/com/aliyuncs/fc/response/ListFunctionsResponse.java index ea04e08..4cd3768 100755 --- a/src/main/java/com/aliyuncs/fc/response/ListFunctionsResponse.java +++ b/src/main/java/com/aliyuncs/fc/response/ListFunctionsResponse.java @@ -28,8 +28,6 @@ */ public class ListFunctionsResponse extends HttpResponse { - private Map header = null; - private FunctionMetadata[] functions = null; private String nextToken = null; @@ -51,23 +49,4 @@ public ListFunctionsResponse setFunctions(FunctionMetadata[] functions) { functions = functions; return this; } - - public Map getHeader() { - return header; - } - - public ListFunctionsResponse setHeader(Map header) { - this.header = header; - return this; - } - - public String getRequestId() { - return header.get("X-Fc-Request-Id"); - } - - public String getEtag() { - return header.get("Etag"); - } - - } diff --git a/src/main/java/com/aliyuncs/fc/response/ListServicesResponse.java b/src/main/java/com/aliyuncs/fc/response/ListServicesResponse.java index 234be06..14d450b 100755 --- a/src/main/java/com/aliyuncs/fc/response/ListServicesResponse.java +++ b/src/main/java/com/aliyuncs/fc/response/ListServicesResponse.java @@ -28,19 +28,9 @@ */ public class ListServicesResponse extends HttpResponse { - private Map header = null; private ServiceMetadata[] services = null; private String nextToken = null; - public Map getHeader() { - return header; - } - - public ListServicesResponse setHeader(Map header) { - this.header = header; - return this; - } - public ServiceMetadata[] getServices() { return services; } @@ -59,12 +49,4 @@ public ListServicesResponse setNextToken(String nextToken) { return this; } - public String getRequestId() { - return header.get("X-Fc-Request-Id"); - } - - public String getEtag() { - return header.get("Etag"); - } - } diff --git a/src/main/java/com/aliyuncs/fc/response/ListTriggersResponse.java b/src/main/java/com/aliyuncs/fc/response/ListTriggersResponse.java index db80a14..1160377 100755 --- a/src/main/java/com/aliyuncs/fc/response/ListTriggersResponse.java +++ b/src/main/java/com/aliyuncs/fc/response/ListTriggersResponse.java @@ -28,24 +28,12 @@ * TODO: add javadoc */ public class ListTriggersResponse extends HttpResponse { - - private Map header; - @SerializedName("triggers") private TriggerMetadata[] triggers; @SerializedName("nextToken") private String nextToken; - public Map getHeader() { - return header; - } - - public ListTriggersResponse setHeader(Map header) { - this.header = header; - return this; - } - public TriggerMetadata[] getTriggers() { return triggers; } @@ -64,12 +52,4 @@ public ListTriggersResponse setNextToken(String nextToken) { return this; } - public String getRequestId() { - return header.get("X-Fc-Request-Id"); - } - - public String getEtag() { - return header.get("Etag"); - } - } diff --git a/src/main/java/com/aliyuncs/fc/response/UpdateFunctionResponse.java b/src/main/java/com/aliyuncs/fc/response/UpdateFunctionResponse.java index f318171..4d02728 100755 --- a/src/main/java/com/aliyuncs/fc/response/UpdateFunctionResponse.java +++ b/src/main/java/com/aliyuncs/fc/response/UpdateFunctionResponse.java @@ -29,18 +29,8 @@ */ public class UpdateFunctionResponse extends HttpResponse { - private Map header; private FunctionMetadata functionMetadata; - public Map getHeader() { - return header; - } - - public UpdateFunctionResponse setHeader(Map header) { - this.header = header; - return this; - } - public FunctionMetadata getFunctionMetadata() { return functionMetadata; } @@ -105,14 +95,6 @@ public String getLastModifiedTime() { return functionMetadata.getLastModifiedTime(); } - public String getRequestId() { - return header.get("X-Fc-Request-Id"); - } - - public String getEtag() { - return header.get("Etag"); - } - public Map getEnvironmentVariables() { Preconditions.checkArgument(functionMetadata != null); return functionMetadata.getEnvironmentVariables(); diff --git a/src/main/java/com/aliyuncs/fc/response/UpdateServiceResponse.java b/src/main/java/com/aliyuncs/fc/response/UpdateServiceResponse.java index 882aeea..176eed6 100755 --- a/src/main/java/com/aliyuncs/fc/response/UpdateServiceResponse.java +++ b/src/main/java/com/aliyuncs/fc/response/UpdateServiceResponse.java @@ -30,18 +30,8 @@ */ public class UpdateServiceResponse extends HttpResponse { - private Map header; private ServiceMetadata serviceMetadata; - public Map getHeader() { - return header; - } - - public UpdateServiceResponse setHeader(Map header) { - this.header = header; - return this; - } - public String getServiceName() { Preconditions.checkArgument(serviceMetadata != null); return serviceMetadata.getServiceName(); @@ -81,13 +71,4 @@ public UpdateServiceResponse setServiceMetadata(ServiceMetadata serviceMetadata) this.serviceMetadata = serviceMetadata; return this; } - - public String getRequestId() { - return header.get("X-Fc-Request-Id"); - } - - public String getEtag() { - return header.get("Etag"); - } - } diff --git a/src/main/java/com/aliyuncs/fc/response/UpdateTriggerResponse.java b/src/main/java/com/aliyuncs/fc/response/UpdateTriggerResponse.java index 6b9c364..b59710b 100755 --- a/src/main/java/com/aliyuncs/fc/response/UpdateTriggerResponse.java +++ b/src/main/java/com/aliyuncs/fc/response/UpdateTriggerResponse.java @@ -29,18 +29,8 @@ */ public class UpdateTriggerResponse extends HttpResponse { - private Map header; private TriggerMetadata triggerMetadata; - public Map getHeader() { - return header; - } - - public UpdateTriggerResponse setHeader(Map header) { - this.header = header; - return this; - } - public UpdateTriggerResponse setTriggerMetadata(TriggerMetadata triggerMetadata) { this.triggerMetadata = triggerMetadata; return this; @@ -80,13 +70,4 @@ public Object getTriggerConfig() { Preconditions.checkArgument(triggerMetadata != null); return triggerMetadata.getTriggerConfig(); } - - public String getRequestId() { - return header.get("X-Fc-Request-Id"); - } - - public String getEtag() { - return header.get("Etag"); - } - } diff --git a/src/test/java/com/aliyuncs/fc/FunctionComputeClientTest.java b/src/test/java/com/aliyuncs/fc/FunctionComputeClientTest.java index d847480..a39d1b3 100644 --- a/src/test/java/com/aliyuncs/fc/FunctionComputeClientTest.java +++ b/src/test/java/com/aliyuncs/fc/FunctionComputeClientTest.java @@ -1,5 +1,8 @@ package com.aliyuncs.fc; +import static com.aliyuncs.fc.model.HttpAuthType.ANONYMOUS; +import static com.aliyuncs.fc.model.HttpAuthType.FUNCTION; +import static com.aliyuncs.fc.model.HttpMethod.*; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertNull; @@ -15,23 +18,7 @@ import com.aliyuncs.fc.exceptions.ClientException; import com.aliyuncs.fc.exceptions.ErrorCodes; import com.aliyuncs.fc.model.*; -import com.aliyuncs.fc.request.CreateFunctionRequest; -import com.aliyuncs.fc.request.CreateServiceRequest; -import com.aliyuncs.fc.request.CreateTriggerRequest; -import com.aliyuncs.fc.request.DeleteFunctionRequest; -import com.aliyuncs.fc.request.DeleteServiceRequest; -import com.aliyuncs.fc.request.DeleteTriggerRequest; -import com.aliyuncs.fc.request.GetFunctionRequest; -import com.aliyuncs.fc.request.GetFunctionCodeRequest; -import com.aliyuncs.fc.request.GetServiceRequest; -import com.aliyuncs.fc.request.GetTriggerRequest; -import com.aliyuncs.fc.request.InvokeFunctionRequest; -import com.aliyuncs.fc.request.ListFunctionsRequest; -import com.aliyuncs.fc.request.ListServicesRequest; -import com.aliyuncs.fc.request.ListTriggersRequest; -import com.aliyuncs.fc.request.UpdateFunctionRequest; -import com.aliyuncs.fc.request.UpdateServiceRequest; -import com.aliyuncs.fc.request.UpdateTriggerRequest; +import com.aliyuncs.fc.request.*; import com.aliyuncs.fc.response.*; import com.aliyuncs.fc.utils.ZipUtils; import com.aliyuncs.http.MethodType; @@ -55,6 +42,7 @@ import java.text.SimpleDateFormat; import java.util.*; +import com.google.gson.JsonObject; import org.json.JSONException; import org.junit.Assert; import org.junit.Before; @@ -92,12 +80,15 @@ public class FunctionComputeClientTest { private static final String FUNCTION_DESC_NEW = "function desc updated"; private static final String TRIGGER_NAME = "testTrigger"; private static final String TRIGGER_TYPE_OSS = "oss"; + private static final String TRIGGER_TYPE_HTTP = "http"; private static final String TRIGGER_TYPE_LOG = "log"; private static final String TRIGGER_TYPE_TIMER = "timer"; public static final String STS_API_VERSION = "2015-04-01"; private FunctionComputeClient client; + private static final Gson gson = new Gson(); + @Before public void setup() { // Create or clean up everything under the test service @@ -109,9 +100,8 @@ public void setup() { GetServiceRequest getSReq = new GetServiceRequest(SERVICE_NAME); try { client.getService(getSReq); - ListFunctionsRequest listFReq = new ListFunctionsRequest(SERVICE_NAME); - ListFunctionsResponse listFResp = client.listFunctions(listFReq); - cleanUpFunctions(SERVICE_NAME, listFResp.getFunctions()); + + cleanUpFunctions(SERVICE_NAME); cleanupService(SERVICE_NAME); } catch (ClientException e) { if (!ErrorCodes.SERVICE_NOT_FOUND.equals(e.getErrorCode())) { @@ -144,7 +134,11 @@ private void cleanupService(String serviceName) { System.out.println("Service " + serviceName + " is deleted"); } - private void cleanUpFunctions(String serviceName, FunctionMetadata[] functions) { + private void cleanUpFunctions(String serviceName) { + ListFunctionsRequest listFReq = new ListFunctionsRequest(SERVICE_NAME); + ListFunctionsResponse listFResp = client.listFunctions(listFReq); + FunctionMetadata[] functions = listFResp.getFunctions(); + for (FunctionMetadata function : functions) { ListTriggersRequest listReq = new ListTriggersRequest(serviceName, function.getFunctionName()); @@ -168,7 +162,13 @@ private void cleanUpTriggers(String serviceName, String functionName, } } - private CreateFunctionResponse createFunction(String functionName) { + private CreateFunctionResponse createFunction(String functionName) throws IOException { + String source = "exports.handler = function(event, context, callback) {\n" + + " callback(null, 'hello world');\n" + + "};"; + + byte[] code = createZipByteData("hello_world.js", source); + CreateFunctionRequest createFuncReq = new CreateFunctionRequest(SERVICE_NAME); createFuncReq.setFunctionName(functionName); createFuncReq.setDescription(FUNCTION_DESC_OLD); @@ -179,7 +179,7 @@ private CreateFunctionResponse createFunction(String functionName) { environmentVariables.put("testKey", "testValue"); createFuncReq.setEnvironmentVariables(environmentVariables); createFuncReq - .setCode(new Code().setOssBucketName(CODE_BUCKET).setOssObjectName(CODE_OBJECT)); + .setCode(new Code().setZipFile(code)); createFuncReq.setTimeout(10); return client.createFunction(createFuncReq); @@ -193,6 +193,15 @@ private CreateServiceResponse createService(String serviceName) { return client.createService(createSReq); } + private CreateTriggerResponse createHttpTrigger(String triggerName, HttpAuthType authType, HttpMethod[] methods) { + CreateTriggerRequest createReq = new CreateTriggerRequest(SERVICE_NAME, FUNCTION_NAME); + createReq.setTriggerName(triggerName); + createReq.setTriggerType(TRIGGER_TYPE_HTTP); + createReq.setTriggerConfig(new HttpTriggerConfig(authType, methods)); + + return client.createTrigger(createReq); + } + private CreateTriggerResponse createOssTrigger(String triggerName, String prefix, String suffix) { CreateTriggerRequest createTReq = new CreateTriggerRequest(SERVICE_NAME, FUNCTION_NAME); createTReq.setTriggerName(triggerName); @@ -278,20 +287,20 @@ public void testNewRegions() { @Test public void testCRUD() - throws ClientException, JSONException, NoSuchAlgorithmException, InterruptedException, ParseException { + throws ClientException, JSONException, NoSuchAlgorithmException, InterruptedException, ParseException, IOException { testCRUDHelper(true); } @Test public void testCRUDStsToken() throws com.aliyuncs.exceptions.ClientException, - ParseException, InterruptedException { + ParseException, InterruptedException, IOException { client = overrideFCClient(true, false); testCRUDHelper(false); } @Test public void testCRUDStsTokenHttps() throws com.aliyuncs.exceptions.ClientException, - ParseException, InterruptedException { + ParseException, InterruptedException, IOException { client = overrideFCClient(true, true); testCRUDHelper(false); } @@ -317,6 +326,96 @@ public void testCreateServiceStsTokenNoPassRole() } } + @Test + public void testCRUDHttpTrigger() throws ParseException, InterruptedException, IOException { + + // create service + CreateServiceResponse createSResp = createService(SERVICE_NAME); + assertEquals(SERVICE_NAME, createSResp.getServiceName()); + + // Create Function + CreateFunctionResponse createFResp = createFunction(FUNCTION_NAME); + + assertFalse(Strings.isNullOrEmpty(createFResp.getRequestId())); + assertFalse(Strings.isNullOrEmpty(createFResp.getFunctionId())); + assertEquals(FUNCTION_NAME, createFResp.getFunctionName()); + assertEquals(FUNCTION_DESC_OLD, createFResp.getDescription()); + + // create http trigger + createHttpTrigger(TRIGGER_NAME, ANONYMOUS, new HttpMethod[] {GET, POST}); + + // List Triggers + ListTriggersRequest listTReq = new ListTriggersRequest(SERVICE_NAME, FUNCTION_NAME); + ListTriggersResponse listTResp = client.listTriggers(listTReq); + + assertFalse(Strings.isNullOrEmpty(listTResp.getRequestId())); + assertEquals(1, listTResp.getTriggers().length); + + TriggerMetadata trigger = listTResp.getTriggers()[0]; + + assertEquals(TRIGGER_NAME, trigger.getTriggerName()); + assertEquals("http", trigger.getTriggerType()); + + // retrieve http trigger + GetTriggerRequest getTReq = new GetTriggerRequest(SERVICE_NAME, FUNCTION_NAME, + TRIGGER_NAME); + + GetTriggerResponse getTResp = client.getTrigger(getTReq); + HttpTriggerConfig triggerConfig = gson + .fromJson(gson.toJson(getTResp.getTriggerConfig()), HttpTriggerConfig.class); + + assertFalse(Strings.isNullOrEmpty(getTResp.getRequestId())); + assertEquals(TRIGGER_NAME, getTResp.getTriggerName()); + assertEquals(TRIGGER_TYPE_HTTP, getTResp.getTriggerType()); + assertTrue(Arrays.deepEquals(new HttpMethod[] {GET, POST}, triggerConfig.getMethods())); + + // update http trigger + GetTriggerResponse triggerOld = getTResp; + HttpTriggerConfig updateTriggerConfig = new HttpTriggerConfig( + FUNCTION, new HttpMethod[] {POST}); + + UpdateTriggerRequest updateTReq = new UpdateTriggerRequest(SERVICE_NAME, FUNCTION_NAME, + TRIGGER_NAME); + updateTReq.setTriggerConfig(updateTriggerConfig); + + Thread.sleep(1000); + + UpdateTriggerResponse updateTResp = updateTrigger(updateTReq); + assertEquals(triggerOld.getTriggerName(), updateTResp.getTriggerName()); + + Gson gson = new Gson(); + HttpTriggerConfig tcOld = gson + .fromJson(gson.toJson(triggerOld.getTriggerConfig()), HttpTriggerConfig.class); + HttpTriggerConfig tcNew = gson + .fromJson(gson.toJson(updateTResp.getTriggerConfig()), HttpTriggerConfig.class); + assertFalse(Arrays.deepEquals(tcOld.getMethods(), tcNew.getMethods())); + assertNotEquals(tcOld.getAuthType(), tcNew.getAuthType()); + + assertEquals(triggerOld.getCreatedTime(), updateTResp.getCreatedTime()); + assertEquals(triggerOld.getTriggerType(), updateTResp.getTriggerType()); + + Date dateOld = DATE_FORMAT.parse(triggerOld.getLastModifiedTime()); + Date dateNew = DATE_FORMAT.parse(updateTResp.getLastModifiedTime()); + + assertTrue(dateOld.before(dateNew)); + + // delete http trigger + deleteTrigger(SERVICE_NAME, FUNCTION_NAME, TRIGGER_NAME); + + getTReq = new GetTriggerRequest(SERVICE_NAME, FUNCTION_NAME, + TRIGGER_NAME); + + try { + client.getTrigger(getTReq); + } catch (ClientException e) { + assertEquals(404, e.getStatusCode()); + } + + + cleanUpFunctions(SERVICE_NAME); + cleanupService(SERVICE_NAME); + } + @Test public void testListServices() { final int numServices = 10; @@ -360,7 +459,7 @@ public void testListServices() { } @Test - public void testListFunctions() { + public void testListFunctions() throws IOException { final int numServices = 10; final int limit = 3; @@ -387,7 +486,7 @@ public void testListFunctions() { assertEquals(numServices / limit + 1, numCalled); } - public void ignoreTestListTriggers() { + public void ignoreTestListTriggers() throws IOException { final int numTriggers = 5; final int limit = 2; @@ -920,27 +1019,16 @@ public void testInvokeFunctionLogTypeAsyncInvalid() throws IOException { fail("ClientException is expected"); } - @Test - public void testCreateFunctionSetZipFile() throws IOException { - createService(SERVICE_NAME); - - // Create a function - CreateFunctionRequest createFuncReq = new CreateFunctionRequest(SERVICE_NAME); - createFuncReq.setFunctionName(FUNCTION_NAME); - createFuncReq.setDescription("Function for test"); - createFuncReq.setMemorySize(128); - createFuncReq.setHandler("hello_world.handler"); - createFuncReq.setRuntime("nodejs4.4"); + private byte[] createZipByteData(String filename, String code) throws IOException { // Setup code directory String tmpDir = "/tmp/fc_test_" + UUID.randomUUID(); - String funcFilePath = tmpDir + "/" + "hello_world.js"; + String funcFilePath = tmpDir + "/" + filename; new File(tmpDir).mkdir(); PrintWriter out = new PrintWriter(funcFilePath); - out.println( - "'use strict'; module.exports.handler = function(event, context, callback) {console.log('hello world'); callback(null, 'hello world');};"); + out.println(code); out.close(); - String zipFilePath = tmpDir + "/" + "hello_world.zip"; + String zipFilePath = tmpDir + "/" + "main.zip"; ZipUtils.zipDir(new File(tmpDir), zipFilePath); File zipFile = new File(zipFilePath); @@ -948,7 +1036,184 @@ public void testCreateFunctionSetZipFile() throws IOException { FileInputStream fis = new FileInputStream(zipFilePath); fis.read(buffer); fis.close(); - Code code = new Code().setZipFile(buffer); + + new File(funcFilePath).delete(); + new File(zipFilePath).delete(); + new File(tmpDir).delete(); + + return buffer; + } + + private void createFunction(String functionName, String handler, String runtime, byte[] data) { + CreateFunctionRequest createFuncReq = new CreateFunctionRequest(SERVICE_NAME); + + Code code = new Code().setZipFile(data); + createFuncReq.setFunctionName(functionName); + createFuncReq.setDescription("test"); + createFuncReq.setHandler(handler); + createFuncReq.setMemorySize(128); + createFuncReq.setRuntime(runtime); + createFuncReq.setCode(code); + createFuncReq.setTimeout(10); + + client.createFunction(createFuncReq); + } + + @Test + public void testHttpInvokeFunction() throws IOException { + createService(SERVICE_NAME); + + // Create a function + String source = "import json\n" + + "\n" + + "def echo_handler(request, response, context):\n" + + "\tresp_body_map = {\n" + + "\t\t\"headers\" : {},\n" + + "\t\t\"queries\" : {},\n" + + "\t\t\"body\" : request.body,\n" + + "\t\t\"path\" : request.path,\n" + + "\t}\n" + + "\n" + + "\tfor headerKey, headerValue in request.headers.items():\n" + + "\t\tresp_body_map[\"headers\"][headerKey] = headerValue\n" + + "\n" + + "\tfor param, value in request.queries.items():\n" + + "\t\tresp_body_map[\"queries\"][param] = value\n" + + "\n" + + "\tbody = json.dumps(resp_body_map)\n" + + "\tresponse.set_body(body)\n" + + "\t\n" + + "\tresponse.set_status_code(200)\n" + + "\t\n" + + "\tresponse.set_header(\"Test-Header-Key\", request.headers[\"Test-Header-Key\"])\n" + + "\tresponse.set_header(\"content-type\", \"application/json\")"; + + byte[] data = createZipByteData("main.py", source); + + // create function + createFunction(FUNCTION_NAME, "main.echo_handler", "python2.7", data); + + + for (HttpAuthType auth : new HttpAuthType[] {ANONYMOUS, FUNCTION}) { + // create http trigger + createHttpTrigger(TRIGGER_NAME, auth, new HttpMethod[] {GET, POST}); + + // Invoke the function + HttpInvokeFunctionRequest request = new HttpInvokeFunctionRequest(SERVICE_NAME, FUNCTION_NAME, auth, POST, "/test/path"); + + request.addQuery("a", "1"); + request.addQuery("b", "2"); + request.addQuery("aaa", null); + + request.setHeader("Test-Header-Key", "testHeaderValue"); + request.setHeader("Content-Type", "application/json"); + + request.setPayload(new String("data").getBytes()); + + InvokeFunctionResponse response = client.invokeFunction(request); + + assertEquals(200, response.getStatus()); + assertTrue(response.getHeader("Content-Type").startsWith("application/json")); + assertEquals("testHeaderValue", response.getHeader("Test-Header-Key")); + + JsonObject jsonObject = gson.fromJson(new String(response.getPayload()), JsonObject.class); + + assertEquals("/test/path", jsonObject.get("path").getAsString()); + assertEquals("1", jsonObject.get("queries").getAsJsonObject().get("a").getAsString()); + assertEquals("2", jsonObject.get("queries").getAsJsonObject().get("b").getAsString()); + assertEquals("data", jsonObject.get("body").getAsString()); + + // delete trigger + deleteTrigger(SERVICE_NAME, FUNCTION_NAME, TRIGGER_NAME); + } + + // Cleanups + client.deleteFunction(new DeleteFunctionRequest(SERVICE_NAME, FUNCTION_NAME)); + client.deleteService(new DeleteServiceRequest(SERVICE_NAME)); + } + + @Test + public void testHttpInvokeFunctionWithoutQueriesAndBody() throws IOException { + createService(SERVICE_NAME); + + // Create a function + String source = "import json\n" + + "\n" + + "def echo_handler(request, response, context):\n" + + "\tresp_body_map = {\n" + + "\t\t\"headers\" : {},\n" + + "\t\t\"queries\" : {},\n" + + "\t\t\"body\" : request.body,\n" + + "\t\t\"path\" : request.path,\n" + + "\t}\n" + + "\n" + + "\tfor headerKey, headerValue in request.headers.items():\n" + + "\t\tresp_body_map[\"headers\"][headerKey] = headerValue\n" + + "\n" + + "\tfor param, value in request.queries.items():\n" + + "\t\tresp_body_map[\"queries\"][param] = value\n" + + "\n" + + "\tbody = json.dumps(resp_body_map)\n" + + "\tresponse.set_body(body)\n" + + "\t\n" + + "\tresponse.set_status_code(200)\n" + + "\t\n" + + "\tresponse.set_header(\"Test-Header-Key\", request.headers[\"Test-Header-Key\"])\n" + + "\tresponse.set_header(\"content-type\", \"application/json\")"; + + byte[] data = createZipByteData("main.py", source); + + // create function + createFunction(FUNCTION_NAME, "main.echo_handler", "python2.7", data); + + + for (HttpAuthType auth : new HttpAuthType[] {ANONYMOUS, FUNCTION}) { + // create http trigger + createHttpTrigger(TRIGGER_NAME, auth, new HttpMethod[] {GET, POST, PUT, HEAD, DELETE}); + + // Invoke the function + HttpInvokeFunctionRequest request = new HttpInvokeFunctionRequest(SERVICE_NAME, FUNCTION_NAME, auth, POST, "/test/path"); + + request.setHeader("Test-Header-Key", "testHeaderValue"); + request.setHeader("Content-Type", "application/json"); + + InvokeFunctionResponse response = client.invokeFunction(request); + + assertEquals(200, response.getStatus()); + assertTrue(response.getHeader("Content-Type").startsWith("application/json")); + assertEquals("testHeaderValue", response.getHeader("Test-Header-Key")); + + JsonObject jsonObject = gson.fromJson(new String(response.getPayload()), JsonObject.class); + + assertEquals("/test/path", jsonObject.get("path").getAsString()); + assertEquals("", jsonObject.get("body").getAsString()); + + // delete trigger + deleteTrigger(SERVICE_NAME, FUNCTION_NAME, TRIGGER_NAME); + } + + // Cleanups + client.deleteFunction(new DeleteFunctionRequest(SERVICE_NAME, FUNCTION_NAME)); + client.deleteService(new DeleteServiceRequest(SERVICE_NAME)); + } + + @Test + public void testCreateFunctionSetZipFile() throws IOException { + createService(SERVICE_NAME); + + String source = "'use strict'; module.exports.handler = function(event, context, callback) {console.log('hello world'); callback(null, 'hello world');};"; + + byte[] data = createZipByteData("hello_world.js", source); + + // Create a function + CreateFunctionRequest createFuncReq = new CreateFunctionRequest(SERVICE_NAME); + createFuncReq.setFunctionName(FUNCTION_NAME); + createFuncReq.setDescription("Function for test"); + createFuncReq.setMemorySize(128); + createFuncReq.setHandler("hello_world.handler"); + createFuncReq.setRuntime("nodejs4.4"); + + Code code = new Code().setZipFile(data); createFuncReq.setCode(code); createFuncReq.setTimeout(10); client.createFunction(createFuncReq); @@ -962,14 +1227,10 @@ public void testCreateFunctionSetZipFile() throws IOException { // Cleanups client.deleteFunction(new DeleteFunctionRequest(SERVICE_NAME, FUNCTION_NAME)); client.deleteService(new DeleteServiceRequest(SERVICE_NAME)); - - new File(zipFilePath).delete(); - new File(funcFilePath).delete(); - new File(tmpDir).delete(); } @Test - public void testInvokeFunctionSetHeader() { + public void testInvokeFunctionSetHeader() throws IOException, InterruptedException { createService(SERVICE_NAME); createFunction(FUNCTION_NAME); @@ -979,6 +1240,9 @@ public void testInvokeFunctionSetHeader() { InvokeFunctionResponse response = client.invokeFunction(request); assertEquals(HttpURLConnection.HTTP_ACCEPTED, response.getStatus()); + + Thread.sleep(5000); + } private Credentials getAssumeRoleCredentials(String policy) @@ -1016,7 +1280,7 @@ private Credentials getAssumeRoleCredentials(String policy) return stsResponse.getCredentials(); } - private void testCRUDHelper(boolean testTrigger) throws ParseException, InterruptedException { + private void testCRUDHelper(boolean testTrigger) throws ParseException, InterruptedException, IOException { // Create Service CreateServiceResponse createSResp = createService(SERVICE_NAME); assertFalse(Strings.isNullOrEmpty(createSResp.getRequestId())); diff --git a/src/test/java/com/aliyuncs/fc/auth/FcSignatureComposerTest.java b/src/test/java/com/aliyuncs/fc/auth/FcSignatureComposerTest.java new file mode 100644 index 0000000..540fbc9 --- /dev/null +++ b/src/test/java/com/aliyuncs/fc/auth/FcSignatureComposerTest.java @@ -0,0 +1,151 @@ +package com.aliyuncs.fc.auth; + +import org.junit.Test; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import static com.aliyuncs.fc.model.HttpMethod.GET; +import static org.junit.Assert.*; + +public class FcSignatureComposerTest { + + @Test + public void testComposeWithoutParameters() { + Map headers = new HashMap(); + headers.put("Content-MD5", "1bca714f406993b309bb87fabeb30a6b"); + headers.put("Content-Type", "application/json"); + headers.put("Date", "today"); + headers.put("X-FC-H6", "k6"); + headers.put("x-fc-h2", "k2"); + headers.put("X-Fc-h1", "k1"); + headers.put("x-fc-h4", "k4"); + headers.put("h3", "k3"); + headers.put("x-fc-h5", "k5"); + + String composed = FcSignatureComposer.composeStringToSign(GET, "aa", headers, null); + + assertEquals("GET\n" + + "1bca714f406993b309bb87fabeb30a6b\n" + + "application/json\n" + + "today\n" + + "x-fc-h1:k1\n" + + "x-fc-h2:k2\n" + + "x-fc-h4:k4\n" + + "x-fc-h5:k5\n" + + "x-fc-h6:k6\n" + + "aa", composed); + } + + @Test + public void testComposeWithMultiValue() { + Map headers = new HashMap(); + headers.put("Content-MD5", "1bca714f406993b309bb87fabeb30a6b"); + headers.put("Content-Type", "application/json"); + headers.put("Date", "today"); + headers.put("X-FC-H6", "k6"); + headers.put("x-fc-h2", "k2"); + headers.put("X-Fc-h1", "k1"); + headers.put("x-fc-h4", "k4"); + headers.put("h3", "k3"); + headers.put("x-fc-h5", "k5"); + + Map queries = new HashMap(); + queries.put("h6", new String[] {"k6"}); + queries.put("h2", new String[] {"k2"}); + queries.put("h1", new String[] {"k4", "k1"}); + queries.put("h4", new String[] {"k44", "k4", "k4"}); + queries.put("h3", new String[] {"k3"}); + queries.put("h5", new String[] {"k5"}); + + String composed = FcSignatureComposer.composeStringToSignWithMultiValue(GET, "aa", headers, queries); + + assertEquals("GET\n" + + "1bca714f406993b309bb87fabeb30a6b\n" + + "application/json\n" + + "today\n" + + "x-fc-h1:k1\n" + + "x-fc-h2:k2\n" + + "x-fc-h4:k4\n" + + "x-fc-h5:k5\n" + + "x-fc-h6:k6\n" + + "aa\n" + + "h1=k1\n" + + "h1=k4\n" + + "h2=k2\n" + + "h3=k3\n" + + "h4=k4\n" + + "h4=k4\n" + + "h4=k44\n" + + "h5=k5\n" + + "h6=k6", composed); + } + + @Test + public void testComposeWithMultiValue2() { + + Map queries = new HashMap(); + queries.put("xyz", new String[] {}); + queries.put("foo", new String[] {"bar"}); + queries.put("key2", new String[] {"123"}); + queries.put("key1", new String[] {"xyz", "abc"}); + queries.put("key3/~x-y_z.a#b", new String[] {"value/~x-y_z.a#b"}); + + String composed = FcSignatureComposer.composeStringToSignWithMultiValue(GET, "/path/action with space", null, queries); + + assertEquals("GET\n" + + "\n" + + "\n" + + "\n" + + "/path/action with space\n" + + "foo=bar\n" + + "key1=abc\n" + + "key1=xyz\n" + + "key2=123\n" + + "key3/~x-y_z.a#b=value/~x-y_z.a#b\n" + + "xyz", composed); + } + + @Test + public void testComposeWithParameters() { + Map headers = new HashMap(); + headers.put("Content-MD5", "1bca714f406993b309bb87fabeb30a6b"); + headers.put("Content-Type", "application/json"); + headers.put("Date", "today"); + headers.put("X-FC-H6", "k6"); + headers.put("x-fc-h2", "k2"); + headers.put("X-Fc-h1", "k1"); + headers.put("x-fc-h4", "k4"); + headers.put("h3", "k3"); + headers.put("x-fc-h5", "k5"); + + Map queries = new HashMap(); + queries.put("h6", "k6"); + queries.put("h2", "k2"); + queries.put("h1", "k1"); + queries.put("h4", "k4"); + queries.put("h3", "k3"); + queries.put("h5", "k5"); + + String composed = FcSignatureComposer.composeStringToSign(GET, "aa", headers, queries); + + assertEquals("GET\n" + + "1bca714f406993b309bb87fabeb30a6b\n" + + "application/json\n" + + "today\n" + + "x-fc-h1:k1\n" + + "x-fc-h2:k2\n" + + "x-fc-h4:k4\n" + + "x-fc-h5:k5\n" + + "x-fc-h6:k6\n" + + "aa\n" + + "h1=k1\n" + + "h2=k2\n" + + "h3=k3\n" + + "h4=k4\n" + + "h5=k5\n" + + "h6=k6", composed); + + } +} \ No newline at end of file diff --git a/src/test/java/com/aliyuncs/fc/request/HttpInvokeFunctionRequestTest.java b/src/test/java/com/aliyuncs/fc/request/HttpInvokeFunctionRequestTest.java new file mode 100644 index 0000000..8c40300 --- /dev/null +++ b/src/test/java/com/aliyuncs/fc/request/HttpInvokeFunctionRequestTest.java @@ -0,0 +1,76 @@ +package com.aliyuncs.fc.request; + +import com.aliyuncs.fc.model.HttpAuthType; +import org.junit.Test; + +import static com.aliyuncs.fc.model.HttpAuthType.ANONYMOUS; +import static com.aliyuncs.fc.model.HttpMethod.GET; +import static java.lang.String.format; +import static org.junit.Assert.*; + +public class HttpInvokeFunctionRequestTest { + + private static final String SERVICE_NAME = "service"; + + private static final String FUNCTION_NAME = "function"; + + @Test + public void testGetPath() { + HttpInvokeFunctionRequest request = createHttpInvoke("a"); + + assertEquals(generatePath("/a"), request.getPath()); + + request = createHttpInvoke("/a/b/c"); + + assertEquals(generatePath("/a/b/c"), request.getPath()); + + request = createHttpInvoke("/a/b/c?a=1&b=2"); + + assertEquals(generatePath("/a/b/c"), request.getPath()); + + request = createHttpInvoke("/a/b/c?a"); + + assertEquals(generatePath("/a/b/c"), request.getPath()); + } + + @Test + public void testGetParamters() { + HttpInvokeFunctionRequest request = createHttpInvoke("a"); + + assertEquals(0, request.getQueryParams().size()); + + request = createHttpInvoke("/a/b/c"); + + assertEquals(0, request.getQueryParams().size()); + + request = createHttpInvoke("/a/b/c?a=1&b=2"); + + assertEquals(2, request.getQueryParams().size()); + assertEquals("1", request.getQueryParams().get("a")); + assertEquals("2", request.getQueryParams().get("b")); + + request = createHttpInvoke("/a/b/c?a"); + + assertEquals(1, request.getQueryParams().size()); + assertEquals("", request.getQueryParams().get("a")); + + request.addQuery("a", "1"); + assertEquals("1", request.getQueryParams().get("a")); + } + + @Test + public void testNullPath() { + HttpInvokeFunctionRequest request = createHttpInvoke(null); + + assertEquals(generatePath("/"), request.getPath()); + } + + private static HttpInvokeFunctionRequest createHttpInvoke(String path) { + return new HttpInvokeFunctionRequest(SERVICE_NAME, FUNCTION_NAME, ANONYMOUS, GET, path); + } + + private static String generatePath(String path) { + return format("/2016-08-15/proxy/service/function%s", path); + } + +} \ No newline at end of file