Skip to main content

Loan Application - Temporal.io based Worker

This tutorial in a nutshell

In this tutorial, we will build a simple Temporal-based Worker in Typescript language that will manage a Loan Application business process in KuFlow. Our example will model a Workflow whose purpose is to fill up an application form for approval. We will include some special rules, such as automatic approval for amounts below a certain quantity, and the use of different currencies which require REST calls to a third-party application.

Prerequisites

Before starting our Worker for the first time, we must register it in KuFlow (app.kuflow.com). After familiarizing yourself with the user interface, you are ready to perform the necessary configurations for our Worker. To do so, click on the Settings menu. If you do not see this menu, you probably do not have the necessary permissions to define processes, so you will have to request it from your Organization administrators, or simply create a FREE new account.

Create the credentials for the Worker

We will configure an APPLICATION that will provide us with the necessary credentials so that our Worker (written in TypeScript and located in your own machine) can interface with KuFlow.

Go to the Settings > Applications menu and click on Add application. We set the name we want and save. Next, you will get the first data needed to configure our Worker.

  • Identifier: Unique identifier of the application. For this tutorial: LoanApp
    • Later in this tutorial, we will configure it in the kuflow.api.client-id property of our example.
  • Token: Password for the application.
    • Later in this tutorial, we will configure it in the kuflow.api.client-secret property of our example.

Finally, you get something like:

Create the process definition

We need to create the definition of the process that will execute our Workflow. In this section, we will configure the KuFlow tasks of which it is made up as well as the information necessary to complete said tasks, the process access rules (i.e. RBAC), as well as another series of information. To do this we go to the Setting > Processes menu and create a new process.

A Process Definition with the following data:

  • Description
    • Free text description about the Workflow.
  • Workflow
    • Workflow Engine
      • KuFlow Engine, because we are designing a Temporal-based Worker.
    • Workflow Application
      • LoanApp, the application to which our Worker will connect to
    • Task queue
      • The name of the Temporal queue where the KuFlow tasks will be set. You can choose any name, later you will set this same name in the appropriate configuration in your Worker. For this tutorial: SampleQueue.
    • Type
      • It must match the name of the TypeScript function of the Workflow. For this tutorial, SampleLoanWorkflow is the name you should type in this input.
  • Metadata
    • Reason metadata: To require the user to indicate a personalized metadata (reason) when starting the process.
      • Code: REASON
      • Name: Reason
      • Field Type: Text
      • Mandatory: Yes
      • Editable: Yes (To allow editing until the process has been completed)
    • Reason metadata: To require the user to indicate a personalized metadata (reason) when starting the process.
      • Code: FIRST_NAME
      • Name: First name
      • Field Type: Text
      • Mandatory: No
      • Editable: No
    • Reason metadata: To require the user to indicate a personalized metadata (reason) when starting the process.
      • Code: LAST_NAME
      • Name: Last name
      • Field Type: Text
      • Mandatory: No
      • Editable: No

We must also define the Permissions

  • At least, one user or group of users must have the role of INITIATOR in order to instantiate the process through the application. In this tutorial, we will allow all users from this organization to be able to process initiation (Groups > Users)
  • At least, one user or group of users will assume the task of loans approvals. In this tutorial, we have defined the user Murph, so we will also give her the role of PROCESS MANAGER.

We define two Structure Definitions in the process with the following data:

  • Structure Data Loan Application Form:
    • Code: SD_LOAN_APPLICATION
    • Schema:
      {
      "title": "Form",
      "type": "object",
      "properties": {
      "FIRST_NAME": {
      "type": "string"
      },
      "LAST_NAME": {
      "type": "string"
      },
      "AMOUNT": {
      "type": "number",
      "minimum": 0
      },
      "CURRENCY": {
      "type": "string",
      "enum": [
      "EUR",
      "USD",
      "GBP"
      ]
      }
      },
      "required": [
      "FIRST_NAME",
      "LAST_NAME",
      "AMOUNT",
      "CURRENCY"
      ]
      }
  • Structure Data Approve Loan Form:
    • Code: SD_APPROVE_LOAN
    • Schema:
      {
      "title": "Form",
      "type": "object",
      "properties": {
      "FIRST_NAME": {
      "type": "string"
      },
      "LAST_NAME": {
      "type": "string"
      },
      "AMOUNT": {
      "type": "number",
      "minimum": 0
      },
      "CURRENCY": {
      "type": "string",
      "enum": [
      "EUR",
      "USD",
      "GBP"
      ]
      }
      },
      "required": [
      "FIRST_NAME",
      "LAST_NAME",
      "AMOUNT",
      "CURRENCY"
      ]
      }

We define two Tasks Definitions in the process with the following data:

  • Task "Loan Application Form"
    • Description: Free text description about the Task.
    • Code: LOAN_APPLICATION
    • Permissions:
      • Groups > Users as Candidate (in this tutorial, we will allow all users from this organization to fill up a loan application form)
    • Structure Data: Loan Application Form
  • Task "Approve Loan"
    • Description: Free text description about the Task.
    • Code: APPROVE_LOAN
    • Permissions:
      • Murph as Candidate
    • Structure Data: Approve Loan Form

We must also define the Permissions

  • At least, one user or group of users must have the role of INITIATOR in order to instantiate the process through the application. In this tutorial, we will allow all users from this organization to be able to process initiation (Default Group)
  • At least, one user or group of users will assume the task of loans approvals. In this tutorial, we have defined the user Murph, so we will also give her the role of PROCESS MANAGER.

Source code for our Worker

The source code for this example can be obtained from our typescript/js examples repository.

However, an alternative methodology could be to download the example template code for the process we are going to implement and compare it with the complete example in our repository. To download the initial template you must access the workflow definition in the App, and once configured, use the download template option.

Main technologies used in the example

This code will serve as a starting point for implementing our Loan Application worker. The requirements for its use are the following:

  • TypeScript JDK
    • You need to have NodeJS installed on your system. The current example code uses version 16+.
  • IDE
    • An IDE with good TypeScript support is necessary to work comfortably. You can use VSCode, IntelliJ Idea, Eclipse or any other with corresponding TypeScript plugins.

To make things simpler, the following dependencies have been used in our example:

  • KuFlow JS SDK
    • Provide some activities and utilities to work with KuFlow.
  • Temporal TypeScript SDK
    • To perform GRPC communications with the KuFlow temporal service.

Implementation details

The workflow

In this section, we will make the fundamental steps to creating a workflow for this business process:

  1. Users in an organization can create a Loan Application.
  2. If the loan is over 5,000 euros, manager approval is required.
  3. The user can indicate the amount requested in several currencies.
  4. A third party API will proceed to convert the currency to Euros at the current time.
  5. The manager is the only authorized user to approve loans.
  6. The user is informed of the final decision on the loan.

We modify the method that creates the Loan Form Application task so that it returns the task to the workflow upon completion.

workflow.ts

async function createProcessItemLoanApplicationForm(workflowRequest: WorkflowRequest): Promise<ProcessItem> {
const processItemId = uuid7()

const processItemRequest: ProcessItemCreateRequest = {
id: processItemId,
type: 'TASK',
processId: workflowRequest.processId,
task: {
taskDefinitionCode: 'LOAN_APPLICATION',
},
}

await createProcessItemAndWaitCompleted(processItemRequest)

const { processItem } = await kuFlowActivities.KuFlow_Engine_retrieveProcessItem({ processItemId })

return processItem
}

async function createProcessItemAndWaitCompleted(processItemCreate: ProcessItemCreateRequest): Promise<void> {
const processItemId = processItemCreate.id
if (processItemId == null) {
throw Error('processItem id is required')
}

await kuFlowActivities.KuFlow_Engine_createProcessItem(processItemCreate)
await condition(() => kuFlowCompletedTaskIds.includes(processItemId))
}

We modify the function that manages the Loan Approval task, so that in its creation the First Name, Last Name fields are completed with the data of the initial task of the form. In the same way, the value for the Amount requested is added, once transformed to Euros from the source currency.

workflow.ts

async function createProcessItemTaskApproveLoan(processItemLoanApplication: ProcessItem, amountEUR: number): Promise<ProcessItem> {
const processItemId = uuid7()

const firstName = `${processItemLoanApplication.task?.data?.value.FIRST_NAME}`
const lastName = `${processItemLoanApplication.task?.data?.value.LAST_NAME}`

const processItemCreate: ProcessItemCreateRequest = {
id: processItemId,
type: 'TASK',
processId: processItemLoanApplication.processId,
task: {
taskDefinitionCode: 'APPROVE_LOAN',
data: {
value: {
FIRST_NAME: firstName,
LAST_NAME: lastName,
AMOUNT: amountEUR.toString(),
},
},
},
}

await createProcessItemAndWaitCompleted(processItemCreate)

const { processItem } = await kuFlowActivities.KuFlow_Engine_retrieveProcessItem({ processItemId })

return processItem
}

The notification tasks (approval or rejection) will be left as they are in the downloaded template.

We need to modify the main method of the workflow with the following content:

workflow.ts

export async function SampleEngineWorkerLoanWorkflow(request: WorkflowRequest): Promise<WorkflowResponse> {
const kuFlowCompletedTaskIds: string[] = []

setHandler(kuFlowEngineSignalProcessItem, (signal: SignalProcessItem) => {
if (signal.type === SignalProcessItemType.TASK) {
kuFlowCompletedTaskIds.push(signal.id)
}
})

log.info('Start', {})

await runWorkflow(request)

log.info('End', {})

return { message: 'OK' }

async function runWorkflow(workflowRequest: WorkflowRequest): Promise<void> {
const processItemLoanApplication = await createProcessItemTaskLoanApplicationForm(workflowRequest)

await updateProcessMetadata(processItemLoanApplication)

const currency = `${processItemLoanApplication.task?.data?.value.CURRENCY}`
const amount = `${processItemLoanApplication.task?.data?.value.AMOUNT}`

const amountEUR = await convertToEuros(currency, amount)

let loanAuthorized = true
if (amountEUR > 5_000) {
const processItemApproveLoan = await createProcessItemTaskApproveLoan(processItemLoanApplication, amountEUR)
const approval = `${processItemApproveLoan.task?.data?.value.APPROVAL}`

loanAuthorized = approval === 'YES'
}

const process = await retrieveProcess(workflowRequest)
if (loanAuthorized) {
await createTaskNotificationGranted(process)
} else {
await createTaskNotificationRejection(process)
}
}
}

The activities

Our workflow interacts with two external systems, the first one is the KuFlow REST API. For this we use a set of activities already provided in the SDK. On the other hand, we need to connect to a third party API that provides real-time currency conversion. For this we create the following activity.

activities.ts

async function currencyConvert(amount: string, from: string, to: string): Promise<string> {
const amountNumber = parseFloat(amount)

const fromTransformed = transformCurrencyCode(from)
const toTransformed = transformCurrencyCode(to)
const endpoint = `https://cdn.jsdelivr.net/gh/fawazahmed0/currency-api@1/latest/currencies/${fromTransformed}/${toTransformed}.json`
const response = await axios.get(endpoint)

const conversion = response.data[toTransformed]

return (amountNumber * conversion).toString()
}

The worker

We need to indicate that our Temporal worker, in addition to implementing the KuFlow activities, implements the activity that connects to a third-party API and obtains a currency transformation.

worker.ts

const workerProperties = loadConfiguration()

// Instantiate KuFlow rest client
const kuFlowRestClient = new KuFlowRestClient(
{
clientId: workerProperties.kuflow.api.clientId,
clientSecret: workerProperties.kuflow.api.clientSecret,
},
{
endpoint: workerProperties.kuflow.api.endpoint,
},
)

// Configure kuflow temporal connection
const kuflowTemporalConnection = await KuflowTemporalConnection.instance({
kuflow: {
restClient: kuFlowRestClient,
},
temporalio: {
connection: {
address: workerProperties.temporal.target,
},
worker: {
taskQueue: workerProperties.temporal.kuflowQueue,
workflowsPath: require.resolve('./workflows'),
activities: {
...createKuFlowActivities(kuFlowRestClient),
...Activities,
},
},
},
})

Runtime.instance().logger.info('Worker connection successfully established')

await kuflowTemporalConnection.runWorker()

await kuflowTemporalConnection.close()

Worker configuration

The last step of this section is filling up the application configuration information. You must complete all the settings and replace the example values. The appropriate values can be obtained from the KuFlow application. Check out the Create the credentials for the Worker section of this tutorial.

# ===================================================================
# PLEASE COMPLETE ALL CONFIGURATIONS BEFORE STARTING THE WORKER
# ===================================================================
kuflow:
api:
# ID of the APPLICATION configured in KUFLOW.
# Get it in "Application details" in the Kuflow APP.
client-id: FILL_ME

# TOKEN of the APPLICATION configured in KUFLOW.
# Get it in "Application details" in the Kuflow APP.
client-secret: FILL_ME

temporal:
# Temporal Queue. Configure it in the "Process definition" in the KUFLOW APP.
kuflow-queue: "SampleQueue"

Please note that this is a YAML, respect the indentation.

We can test this scenario, initiating the process several times, in order to create different applications and approvals

Summary

In this tutorial, we have covered the basics of creating a workflow in KuFlow for loan applications. We have defined a new process definition and we have built a workflow that contemplates the required business rules defined above.

We sincerely hope that this step-by-step guide will help you to understand better how KuFlow can help your business to have better and more solid business processes.

Kuflow Logo