12.2. Developing Quarkus functions
After you have created a Quarkus function project, you can modify the template files provided to add business logic to your function. This includes configuring function invocation and the returned headers and status codes.
12.2.1. Prerequisites 링크 복사링크가 클립보드에 복사되었습니다!
- Before you can develop functions, you must complete the setup steps in Configuring OpenShift Serverless Functions.
12.2.2. Quarkus function template structure 링크 복사링크가 클립보드에 복사되었습니다!
When you create a Quarkus function by using the Knative (kn) CLI, the project directory looks similar to a typical Maven project. Additionally, the project contains the func.yaml file, which is used for configuring the function.
Both http and event trigger functions have the same template structure:
Template structure
.
├── func.yaml
├── mvnw
├── mvnw.cmd
├── pom.xml
├── README.md
└── src
├── main
│ ├── java
│ │ └── functions
│ │ ├── Function.java
│ │ ├── Input.java
│ │ └── Output.java
│ └── resources
│ └── application.properties
└── test
└── java
└── functions
├── FunctionTest.java
└── NativeFunctionIT.java
- 1
- Used to determine the image name and registry.
- 2
- The Project Object Model (POM) file contains project configuration, such as information about dependencies. You can add additional dependencies by modifying this file.
Example of additional dependencies
... <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>3.8.0</version> <scope>test</scope> </dependency> </dependencies> ...Dependencies are downloaded during the first compilation.
- 3
- The function project must contain a Java method annotated with
@Funq. You can place this method in theFunction.javaclass. - 4
- Contains simple test cases that can be used to test your function locally.
12.2.3. About invoking Quarkus functions 링크 복사링크가 클립보드에 복사되었습니다!
You can create a Quarkus project that responds to cloud events, or one that responds to simple HTTP requests. Cloud events in Knative are transported over HTTP as a POST request, so either function type can listen and respond to incoming HTTP requests.
When an incoming request is received, Quarkus functions are invoked with an instance of a permitted type.
| Invocation method | Data type contained in the instance | Example of data |
|---|---|---|
| HTTP POST request | JSON object in the body of the request |
|
| HTTP GET request | Data in the query string |
|
|
|
JSON object in the |
|
The following example shows a function that receives and processes the customerId and productId purchase data that is listed in the previous table:
Example Quarkus function
public class Functions {
@Funq
public void processPurchase(Purchase purchase) {
// process the purchase
}
}
The corresponding Purchase JavaBean class that contains the purchase data looks as follows:
Example class
public class Purchase {
private long customerId;
private long productId;
// getters and setters
}
12.2.3.1. Invocation examples 링크 복사링크가 클립보드에 복사되었습니다!
The following example code defines three functions named withBeans, withCloudEvent, and withBinary;
Example
import io.quarkus.funqy.Funq;
import io.quarkus.funqy.knative.events.CloudEvent;
public class Input {
private String message;
// getters and setters
}
public class Output {
private String message;
// getters and setters
}
public class Functions {
@Funq
public Output withBeans(Input in) {
// function body
}
@Funq
public CloudEvent<Output> withCloudEvent(CloudEvent<Input> in) {
// function body
}
@Funq
public void withBinary(byte[] in) {
// function body
}
}
The withBeans function of the Functions class can be invoked by:
An HTTP POST request with a JSON body:
$ curl "http://localhost:8080/withBeans" -X POST \ -H "Content-Type: application/json" \ -d '{"message": "Hello there."}'An HTTP GET request with query parameters:
$ curl "http://localhost:8080/withBeans?message=Hello%20there." -X GETA
CloudEventobject in binary encoding:$ curl "http://localhost:8080/" -X POST \ -H "Content-Type: application/json" \ -H "Ce-SpecVersion: 1.0" \ -H "Ce-Type: withBeans" \ -H "Ce-Source: cURL" \ -H "Ce-Id: 42" \ -d '{"message": "Hello there."}'A
CloudEventobject in structured encoding:$ curl http://localhost:8080/ \ -H "Content-Type: application/cloudevents+json" \ -d '{ "data": {"message":"Hello there."}, "datacontenttype": "application/json", "id": "42", "source": "curl", "type": "withBeans", "specversion": "1.0"}'
The withCloudEvent function of the Functions class can be invoked by using a CloudEvent object, similarly to the withBeans function. However, unlike withBeans, withCloudEvent cannot be invoked with a plain HTTP request.
The withBinary function of the Functions class can be invoked by:
A
CloudEventobject in binary encoding:$ curl "http://localhost:8080/" -X POST \ -H "Content-Type: application/octet-stream" \ -H "Ce-SpecVersion: 1.0"\ -H "Ce-Type: withBinary" \ -H "Ce-Source: cURL" \ -H "Ce-Id: 42" \ --data-binary '@img.jpg'A
CloudEventobject in structured encoding:$ curl http://localhost:8080/ \ -H "Content-Type: application/cloudevents+json" \ -d "{ \"data_base64\": \"$(base64 --wrap=0 img.jpg)\", \"datacontenttype\": \"application/octet-stream\", \"id\": \"42\", \"source\": \"curl\", \"type\": \"withBinary\", \"specversion\": \"1.0\"}"
12.2.4. CloudEvent attributes 링크 복사링크가 클립보드에 복사되었습니다!
If you need to read or write the attributes of a CloudEvent, such as type or subject, you can use the CloudEvent<T> generic interface and the CloudEventBuilder builder. The <T> type parameter must be one of the permitted types.
In the following example, CloudEventBuilder is used to return success or failure of processing the purchase:
public class Functions {
private boolean _processPurchase(Purchase purchase) {
// do stuff
}
public CloudEvent<Void> processPurchase(CloudEvent<Purchase> purchaseEvent) {
System.out.println("subject is: " + purchaseEvent.subject());
if (!_processPurchase(purchaseEvent.data())) {
return CloudEventBuilder.create()
.type("purchase.error")
.build();
}
return CloudEventBuilder.create()
.type("purchase.success")
.build();
}
}
12.2.5. Quarkus function return values 링크 복사링크가 클립보드에 복사되었습니다!
Functions can return an instance of any type from the list of permitted types. Alternatively, they can return the Uni<T> type, where the <T> type parameter can be of any type from the permitted types.
The Uni<T> type is useful if a function calls asynchronous APIs, because the returned object is serialized in the same format as the received object. For example:
- If a function receives an HTTP request, then the returned object is sent in the body of an HTTP response.
-
If a function receives a
CloudEventobject in binary encoding, then the returned object is sent in the data property of a binary-encodedCloudEventobject.
The following example shows a function that fetches a list of purchases:
Example command
public class Functions {
@Funq
public List<Purchase> getPurchasesByName(String name) {
// logic to retrieve purchases
}
}
- Invoking this function through an HTTP request produces an HTTP response that contains a list of purchases in the body of the response.
-
Invoking this function through an incoming
CloudEventobject produces aCloudEventresponse with a list of purchases in thedataproperty.
12.2.5.1. Permitted types 링크 복사링크가 클립보드에 복사되었습니다!
The input and output of a function can be any of the void, String, or byte[] types. Additionally, they can be primitive types and their wrappers, for example, int and Integer. They can also be the following complex objects: Javabeans, maps, lists, arrays, and the special CloudEvents<T> type.
Maps, lists, arrays, the <T> type parameter of the CloudEvents<T> type, and attributes of Javabeans can only be of types listed here.
Example
public class Functions {
public List<Integer> getIds();
public Purchase[] getPurchasesByName(String name);
public String getNameById(int id);
public Map<String,Integer> getNameIdMapping();
public void processImage(byte[] img);
}
12.2.6. Testing Quarkus functions 링크 복사링크가 클립보드에 복사되었습니다!
Quarkus functions can be tested locally on your computer. In the default project that is created when you create a function using kn func create, there is the src/test/ directory, which contains basic Maven tests. These tests can be extended as needed.
Prerequisites
- You have created a Quarkus function.
-
You have installed the Knative (
kn) CLI.
Procedure
- Navigate to the project folder for your function.
Run the Maven tests:
$ ./mvnw test
12.2.7. Overriding liveness and readiness probe values 링크 복사링크가 클립보드에 복사되었습니다!
You can override liveness and readiness probe values for your Quarkus functions. This allows you to configure health checks performed on the function.
Prerequisites
- The OpenShift Serverless Operator and Knative Serving are installed on the cluster.
-
You have installed the Knative (
kn) CLI. -
You have created a function by using
kn func create.
Procedure
Override the
/health/livenessand/health/readinesspaths with your own values. You can do this either by changing properties in the function source or by setting theQUARKUS_SMALLRYE_HEALTH_LIVENESS_PATHandQUARKUS_SMALLRYE_HEALTH_READINESS_PATHenvironment variables onfunc.yamlfile.To override the paths using the function source, update the path properties in the
src/main/resources/application.propertiesfile:quarkus.smallrye-health.root-path=/health1 quarkus.smallrye-health.liveness-path=alive2 quarkus.smallrye-health.readiness-path=ready3 To override the paths using environment variables, define the path variables in the
buildblock of thefunc.yamlfile:build: builder: s2i buildEnvs: - name: QUARKUS_SMALLRYE_HEALTH_LIVENESS_PATH value: alive1 - name: QUARKUS_SMALLRYE_HEALTH_READINESS_PATH value: ready2
Add the new endpoints to the
func.yamlfile, so that they are properly bound to the container for the Knative service:deploy: healthEndpoints: liveness: /health/alive readiness: /health/ready