User registration

User registration example illustrates Workflow as a Function Flow concept that breaks down a workflow definition into functions and automatically invokes them by emitting events (in cloud event format).

This example takes a simple user registration use case and implements it as workflow. The logic behind is composed of

  • validating provided user information

  • generating username and password

  • verifying that given user name is not yet registered in the Swagger PetStore service

  • creating user in the Swagger PetStore service

So the workflow defines multiple types of operations that are being invoked, starting from java services implemented within the service and finishing at REST invocation based on OpenAPI definitions.

In addition to that REST calls are equipped with error handling to tackle unexpected responses like server errors during creation of a user in Swagger PetStore service or expected situations like user not found in Swagger PetStore service.

examples user registration

Run it

There are multiple paths that can be taken during the user registration

user registration workflow

Here is the functions that are broken out of the workflow

user registration workflow functions

A surprising might be that the Notify invalid data is not selected as a function. The reason is that the Notify invalid data is combined with Verify user data into single function to show case ability of being able to control which activities are included in single function.

user registration workflow ff continue

To be able to run it a Kubernetes cluster must be available with KNative Eventing installed. It could be local installation, such as

  • Minikube

  • Kind

or it can be cloud based Kubernetes cluster like

  • Google Kubernetes Engine

  • OpenShift

  • Azure kubernetes Engine

  • others

To install KNative, look at official documentation

Once the Knative and Kubernates cluster is available you can deploy the example by invoking following commands

kubectl apply -f k8s/user-registration.yaml

Here is a content of the file for quick reference

apiVersion: sources.knative.dev/v1beta1
kind: SinkBinding
metadata:
  name: bind-user-registration
spec:
  subject:
    apiVersion: serving.knative.dev/v1
    kind: Service
    name: user-registration

  sink:
    ref:
      apiVersion: eventing.knative.dev/v1
      kind: Broker
      name: default
---
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: user-registration
spec:
  template:
    metadata:
      name: user-registration-v1
      annotations:
        autoscaling.knative.dev/target: "1"
    spec:
      containers:
        - image: automatiko/user-registration

---
apiVersion: eventing.knative.dev/v1
kind: Trigger
metadata:
  name: userregistration
spec:
  broker: default
  filter:
    attributes:
      type: io.automatiko.examples.userRegistration
  subscriber:
    ref:
      apiVersion: serving.knative.dev/v1
      kind: Service
      name: user-registration

---
apiVersion: eventing.knative.dev/v1
kind: Trigger
metadata:
  name: getuser
spec:
  broker: default
  filter:
    attributes:
      type: io.automatiko.examples.userRegistration.getuser
  subscriber:
    ref:
      apiVersion: serving.knative.dev/v1
      kind: Service
      name: user-registration

---
apiVersion: eventing.knative.dev/v1
kind: Trigger
metadata:
  name: notregistered
spec:
  broker: default
  filter:
    attributes:
      type: io.automatiko.examples.userRegistration.notregistered
  subscriber:
    ref:
      apiVersion: serving.knative.dev/v1
      kind: Service
      name: user-registration

---
apiVersion: eventing.knative.dev/v1
kind: Trigger
metadata:
  name: generateusernameandpassword
spec:
  broker: default
  filter:
    attributes:
      type: io.automatiko.examples.userRegistration.generateusernameandpassword
  subscriber:
    ref:
      apiVersion: serving.knative.dev/v1
      kind: Service
      name: user-registration

---
apiVersion: eventing.knative.dev/v1
kind: Trigger
metadata:
  name: registeruser
spec:
  broker: default
  filter:
    attributes:
      type: io.automatiko.examples.userRegistration.registeruser
  subscriber:
    ref:
      apiVersion: serving.knative.dev/v1
      kind: Service
      name: user-registration

---
apiVersion: eventing.knative.dev/v1
kind: Trigger
metadata:
  name: notifyregistered
spec:
  broker: default
  filter:
    attributes:
      type: io.automatiko.examples.userRegistration.notifyregistered
  subscriber:
    ref:
      apiVersion: serving.knative.dev/v1
      kind: Service
      name: user-registration

---
apiVersion: eventing.knative.dev/v1
kind: Trigger
metadata:
  name: notifyservererror
spec:
  broker: default
  filter:
    attributes:
      type: io.automatiko.examples.userRegistration.notifyservererror
  subscriber:
    ref:
      apiVersion: serving.knative.dev/v1
      kind: Service
      name: user-registration

---
apiVersion: eventing.knative.dev/v1
kind: Trigger
metadata:
  name: servererror
spec:
  broker: default
  filter:
    attributes:
      type: io.automatiko.examples.userRegistration.servererror
  subscriber:
    ref:
      apiVersion: serving.knative.dev/v1
      kind: Service
      name: user-registration

Optionally you can also deploy event displayer to see all events flowing through the Knative broker

kubectl apply -f - << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: event-display
spec:
  replicas: 1
  selector:
    matchLabels: &labels
      app: event-display
  template:
    metadata:
      labels: *labels
    spec:
      containers:
        - name: event-display
          image: gcr.io/knative-releases/knative.dev/eventing/cmd/event_display

---

kind: Service
apiVersion: v1
metadata:
  name: event-display
spec:
  selector:
    app: event-display
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
EOF

Deploy the trigger for event displayer that will simply consume all events as there is no filter defined

kubectl apply -f - << EOF
apiVersion: eventing.knative.dev/v1
kind: Trigger
metadata:
  name: event-display
spec:
  broker: default
  subscriber:
    ref:
     apiVersion: v1
     kind: Service
     name: event-display
EOF

Get the url of the default broker use following command

kubectl get broker default

which should produce similar output

NAME      URL                                                                                AGE    READY   REASON
default   http://broker-ingress.knative-eventing.svc.cluster.local/knativetutorial/default   140d   True

The happy path

Happy path consists of steps that will lead to successful user registration.

Try it

Follow steps in the Details section to see the happy path in action.

Login to a curler pod that enables an easy access to the broker to send requests as it might not be exposed to external traffic (e.g. ingress). If your Knative broker is exposed to external traffic you can skip the curler step.

Issue following curl command from the pod running within cluster so the broker url will be properly resolved.

curl -v "http://broker-ingress.knative-eventing.svc.cluster.local/knativetutorial/default" \
-X POST \
-H "Ce-Id: 1234" \
-H "Ce-Specversion: 1.0" \
-H "Ce-Type: io.automatiko.examples.userRegistration" \
-H "Ce-Source: curl" \
-H "Content-Type: application/json" \
-d '{"user" : {"email" : "mike.strong@email.com",  "firstName" : "mike",  "lastName" : "strong"}}'

This will send a request to the broker using HTTP binary binding for cloud events. The cloud events information are given http headers prefixed with ce-.

Taking into consideration that this request was sent for the first time it should register user in Swagger PetStore.

It might result in a already registered when the user was already registered so consider updating the first name and last name in the request payload with custom data that will ensure new user

The invalid data path

Invalid data path consists of steps that will lead to fast finish without user registration.

Try it

Follow steps in the Details section to see the invalid data path in action.

Login to a curler pod that enables an easy access to the broker to send requests as it might not be exposed to external traffic (e.g. ingress). If your Knative broker is exposed to external traffic you can skip the curler step.

Issue following curl command from the pod running within cluster so the broker url will be properly resolved.

curl -v "http://broker-ingress.knative-eventing.svc.cluster.local/knativetutorial/default" \
-X POST \
-H "Ce-Id: 1234" \
-H "Ce-Specversion: 1.0" \
-H "Ce-Type: io.automatiko.examples.userRegistration" \
-H "Ce-Source: curl" \
-H "Content-Type: application/json" \
-d '{"user" : {"email" : "mike.strong@email.com",  "firstName" : "mike",  "lastName" : ""}}'

This will send a request to the broker using HTTP binary binding for cloud events. The cloud events information are given http headers prefixed with ce-.

Since user’s last name is not set the workflow will reject processing due to invalid data

The already registered path

Already registered path consists of steps that will lead to fast finish without user registration.

Try it

Follow steps in the Details section to see the already registered path in action.

Login to a curler pod that enables an easy access to the broker to send requests as it might not be exposed to external traffic (e.g. ingress). If your Knative broker is exposed to external traffic you can skip the curler step.

Issue following curl command from the pod running within cluster so the broker url will be properly resolved. Main rule here is that there should be already user with same first and last name registered. For example that the happy path has been executed.

curl -v "http://broker-ingress.knative-eventing.svc.cluster.local/knativetutorial/default" \
-X POST \
-H "Ce-Id: 1234" \
-H "Ce-Specversion: 1.0" \
-H "Ce-Type: io.automatiko.examples.userRegistration" \
-H "Ce-Source: curl" \
-H "Content-Type: application/json" \
-d '{"user" : {"email" : "mike.strong@email.com",  "firstName" : "mike",  "lastName" : "strong"}}'

This will send a request to the broker using HTTP binary binding for cloud events. The cloud events information are given http headers prefixed with ce-.

Since user was already registered processing is stopped