Executor Service

The Executor service provides a flexible and efficient mechanism for automating tasks and workflows within a system. It consists of two main properties: Trigger and Target, which allows you to define when and what actions to execute.

Trigger

The Trigger property defines the conditions under which the Executor service initiates an action. It specifies the event type and condition that must be met for the action to be triggered.

You can configure the following three types of triggers to execute the user defined tasks.

1. Event-based trigger

In this trigger, you can specify the type of event that initiates it (e.g., Tailor DB data update and StateFlow creation) and outline the specific conditions or criteria for its execution.

Refer Supported Events to learn about the different types of events supported in the Tailor Platform. For setup instructions, see Event based trigger.

2. Incoming webhook trigger

You can configure your API endpoint (webhook URL) for receiving incoming data. See Incoming webhook trigger for setup instructions.

3. Schedule-based trigger

This trigger can schedule tasks at specified intervals. For setup instructions, see Schedule based trigger.

Target

The Target property defines the action to be executed when the Trigger condition is met. You can describe the specific task or operation to be performed, such as querying Tailor DB, sending notifications, or triggering workflows. There are three types of targets:

1. TailorGraphql

This involves interaction with the Tailor DB and performs operations such as querying data or making changes (mutations).

2. Webhook

With this type of target, you can send a HTTP request from your executor service. Currently we only support POST method.

3. Job Function

This target type executes JavaScript/TypeScript code asynchronously via the Function Service. Job functions are typically used for asynchronous task processing that doesn't need immediate response, long-running operations that exceed synchronous request timeouts, and background processing triggered by events or schedules. The job function execution returns an execution ID for tracking, unlike synchronous functions that return results directly.

Each Trigger can be configured with the above three target types, providing nine ways to configure them. Both TargetTailorGraphql and TargetWebhook have a timeout set to 60 seconds. If the process exceeds this limit, it is considered a failure, and a retry will be triggered (up to a maximum of 10 attempts). You can view the number of attempts for each job created for an executor in the Tailor Console. For more details on the number of attempts, refer to the Verify the trigger section of the event based trigger tutorial.

Examples

Below you can find examples of the three types of Trigger.

Event-based Trigger

1. Event type: tailordb.type_record.created

The following trigger is based on event type tailordb.type_record.created and operation type tailor_graphql. For every new PurchaseOrder record created in the Tailor DB, a PutAway record is created.

executor.tf

resource "tailor_executor" "event_based_executor" {
  workspace_id = tailor_workspace.ims.id
  name         = "event-based-executor"
  description  = "Execute query based on an event"

  trigger = {
    event = {
      type      = "tailordb.type_record.created"
      condition = <<EOF
        args.namespaceName == "ims" && args.typeName == "Category"
      EOF
    }
  }

  operation = {
    tailor_graphql = {
      app_name = tailor_application.ims.name
      invoker = {
        event_user = true
      }
      query     = <<EOF
        mutation createProduct($categoryID: ID!, $title: String!) {
          createProduct(input: {
            categoryID: $categoryID
            title: $title
          }) {
            id
          }
        }
      EOF
      variables = <<EOF
      ({
        "categoryID": args.newRecord.id,
        "title": args.newRecord.name + " Product"
      })
      EOF
    }
  }
}

The sample trigger below is configured with the operation type webhoook. When a new product is created in the Tailor DB, the executor triggers a Slack notification.

executor_webhook.tf

resource "tailor_executor" "slack_notification" {
  workspace_id = tailor_workspace.ims.id
  name         = "slack-notification"
  description  = "notify slack when a new product is created"

  trigger = {
    event = {
  	type   = "tailordb.type_record.created"
  	condition =  <<EOF
        args.namespaceName == "ims" && args.typeName == "Product"
      EOF
    }
  }

  operation = {
    webhook = {
  	url = <<EOF
  	"https://hooks.slack.com/services/yourServiceName"
  	EOF
  	headers = [
  	  {
  		key       = "Content-Type"
  		raw_value = "application/json"
  	  }
  	]
  	body = <<EOF
  	  ({
  		  "text": "New Product Registration :tada: " + args.newRecord.title
  	  })
  	EOF
  	}
    }
  }

The following trigger is based on event type tailordb.type_record.created and target type #TargetTailorGraphql. For every new PurchaseOrder record created in the Tailor DB, a PutAway record is created.

#eventBasedExecutor: executor.#Executor & {
	Name:        "eventbased-executor"
	Description: "Create a data based on the event"
	Trigger: executor.#TriggerEvent & {
		EventType: "tailordb.type_record.created"
		Condition: common.#Script & {
			Expr: """
				args.namespaceName == "my-tailordb" && args.typeName == "PurchaseOrder"
				"""
		}
	}
	Target: executor.#TargetTailorGraphql & {
		AppName: "my-app"
		Invoker: settings.adminInvoker
		Query: """
			mutation ($input: createPutAwayWithEventInput!) {
				createPutAwayWithEvent(input: $input)
			}"""
		Variables: common.#Script & {
			Expr: """
			({
				input: {
					"quantity": args.newRecord.quantity,
					"purchaseOrderID": args.newRecord.id,
					"putAwayDate": args.newRecord.purchaseOrderDate
				}
			})"""
		}
	}
}

The sample trigger below is configured with the target type #TargetWebhook. When a new product is created in the Tailor DB, the executor triggers a Slack notification.

#slackNotification: executor.#Executor & {
	Name:        "slacknotification"
	Description: "notify slack when new product is created"
	Trigger: executor.#TriggerEvent & {
		EventType: "tailordb.type_record.created"
		Condition: common.#Script & {
			Expr: """
				args.namespaceName == "my-tailordb" && args.typeName == "Product"
				"""
		}
	}
	Target: executor.#TargetWebhook & {
		URL: common.#Script & {
			Expr: "\"https://hooks.slack.com/services/{service-name}/\""
		}
		Headers: [
			{
				Key:   "Content-type"
				Value: "application/json"
			}
		]
		Body: common.#Script & {
			Expr: """
			({
				"text": "New Product Registration :tada:",
				"blocks": [
					{
						"type": "section",
						"text": {
							"type": "mrkdwn",
							"text": "*New Product Registration* :tada: " + args.newRecord.name
						}
					}
				]
			})"""
		}
	}
}

2. Event type: pipeline.resolver.executed

The examples below demonstrate how to configure an executor to trigger based on the pipeline.resolver.executed event.

When the resolver createCategoryForProduct succeeds, the executor will create a new product in the Tailor DB. Here, the trigger condition evaluates the value of args.succeeded to determine whether to create the product.

executor_evenbased.tf

resource "tailor_executor" "eventbased_executor_pipeline_1" {
  workspace_id = tailor_workspace.ims.id
  name         = "eventbased-executor-pipeline-1"
  description  = "Create new product based on a successful event"

  trigger = {
    event = {
      type   = "pipeline.resolver.executed"
      condition = <<EOF
        args.namespaceName == "ims" && args.resolverName == "createCategoryForProduct" && args.succeeded != null
      EOF
    }
  }

  operation = {
    tailor_graphql = {
      app_name = tailor_application.ims.name
      invoker = {
        event_user = true
      }
    query     = <<EOF
      mutation createProduct($title: String!, $description: String!) {
  	  createProduct(input: {title: $title, description: $description}) {
  	    id
  	  }
      }
    EOF
    variables = <<EOF
      ({
  	    "title": args.succeeded.result.pipelines.createProduct.id,
  	    "description": "Created with a trigger"
      })
    EOF
    }
  }
}

In the following example, when the resolver createCategoryForProduct fails, the executor will create a new product in the Tailor DB. Here, the trigger condition evaluates the value of args.failed to determine whether to create the product.

executor_evenbased.tf

resource "tailor_executor" "eventbased_executor_pipeline_2" {
	workspace_id = tailor_workspace.ims.id
	name         = "eventbased-executor-pipeline-2"
	description  = "Create new product based on a failed pipeline"

  trigger = {
    event = {
  	type   = "pipeline.resolver.executed"
  	condition = <<EOF
  		args.namespaceName == "ims" && args.resolverName == "createCategoryForProduct" && args.failed != null
  	EOF
    }
  }

  operation = {
    tailor_graphql = {
      app_name = tailor_application.ims.name
      invoker = {
        event_user = true
      }
    query     = <<EOF
      mutation createProduct($title: String!) {
  	  createProduct(input: {title: $title}) {
  	    id
  	  }
      }
    EOF
    variables = <<EOF
      ({
  	    "title": args.failed.error
      })
    EOF
    }
  }
}

The properties of an executor with an event based trigger are defined as follows:

  • name: The name of the executor. The name field has the validation rule ^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$, and it does not allow capital letters.
  • workspace_id: The ID of the workspace that the executor namespace belongs to.
  • description: The description of the executor.
  • trigger: The type of trigger ex: event.
    • event:
      • type: The type of event ex: tailordb.type_record.created
      • condition: The condition for the job to get executed.
  • operation:
    • tailor_graphql
      • app_name: The name of the app.
      • query: The query to be executed.
      • variables: The input variables for the query.
      • invoker: The user role may be required to execute the query.
    • webhook
      • url: The URL of the API endpoint.
      • body:The payload or message to be included in the request.
      • headers:
        • key: The key in a request header.
        • raw_value: The value for a request header key. In the Value, you can leverage secret manager.
        • secret: Secret is the key to generate tailor-signature-256 signature in your header. You need to use secret manager to store the secret key.
          • vault_name: The name of the vault.
          • secret_name: The key that stores the secret.
    • function
      • name: The name of the function.
      • script: The script to execute.
      • variables: The variables to pass to the function.
      • invoker: The invoker of the operation.

Refer to the Tailor Platform Provider documentation for more details on executor properties.

When the resolver createShipmentFromSalesOrder succeeds, the executor will create a new product in the Tailor DB. Here, the trigger condition evaluates the value of args.succeeded to determine whether to create the product.

#triggerCreateShipmentFromSalesOrder: executor.#Executor & {
	Name:        "eventbased-executor-2"
	Description: "Create new product based on the event"
	Trigger: executor.#TriggerEvent & {
		EventType: "pipeline.resolver.executed"
		Condition: common.#Script & {
			Expr: """
				args.namespaceName == "my-pipeline" && args.resolverName == "createShipmentFromSalesOrder" && args.succeeded != null
				"""
		}
	}
	Target: executor.#TargetTailorGraphql & {
		AppName: "my-app"
		Query: """
			mutation createProduct($title: String!, $description: String!) {
				createProduct(input: {title: $title, description: $description}) {
					id
				}
			}"""
		Variables: common.#Script & {
			Expr: """
			({
				"title": args.succeeded.result.pipelines.createShipment.id,
				"description": "Created with a trigger"
			})"""
		}
	}
}

In the following example, when the resolver createShipmentFromSalesOrder fails, the executor will create a new product in the Tailor DB. Here, the trigger condition evaluates the value of args.failed to determine whether to create the product.

#triggerCreateShipmentFromSalesOrderWithError: executor.#Executor & {
	Name:        "eventbased-executor-3"
	Description: "Create new product based on the event"
	Trigger: executor.#TriggerEvent & {
		EventType: "pipeline.resolver.executed"
		Condition: common.#Script & {
			Expr: """
				args.namespaceName == "my-pipeline" && args.resolverName == "createShipmentFromSalesOrder" && args.failed != null
				"""
		}
	}
	Target: executor.#TargetTailorGraphql & {
		AppName: "my-app"
		Query: """
			mutation createProduct($title: String!) {
				createProduct(input: {title: $title}) {
					id
				}
			}"""
		Variables: common.#Script & {
			Expr: """
			({
				"title": args.failed.error
			})"""
		}
	}
}

The properties of an executor with an event based trigger are defined as follows:

  • Name: The name of the executor. The name field has the validation rule ^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$, and it does not allow capital letters.
  • Description: The description of the executor.
  • Trigger: The type of trigger ex: #TriggerEvent.
  • EventType: The type of event ex: tailordb.type_record.created
  • Condition: The condition for the job to get executed.
  • Target:
  • TargetTailorGraphql
    • AppName: The name of the app.
    • Query: The query to be executed.
    • Variables: The input variables for the query.
    • Invoker: The user role may be required to execute the query.
  • TargetWebhook
    • URL: The URL of the API endpoint.
    • Body:The payload or message to be included in the request.
    • Headers:
      • Key: The key in a request header.
      • Value: The value for a request header key. In the Value, you can leverage secret manager.
    • Secret: Secret is the key to generate tailor-signature-256 signature in your header. You need to use secret manager to store the secret key.
      • VaultName: The name of the vault.
      • SecretKey: The key that stores the secret.

Schedule-based Trigger

Schedule-based triggers allow automated execution of tasks at specified intervals, ensuring seamless and timely operations.

In the following example, a scheduled event triggers an HTTP request to a specified webhook URL every minute.

executor_scheduled_event.tf

resource "tailor_executor" "scheduled_event_executor" {
  workspace_id = tailor_workspace.ims.id
  name         = "scheduled-event-executor"
  description  = "execute query after an event"

  trigger = {
    schedule = {
	  frequency = "* * * * *"
	  timezone = "UTC"
	}
  }

  operation = {
    webhook = {
      url = <<EOF
	  "http://localhost"
	  EOF
	  headers = [
    	{
      	  key       = "Content-Type"
          raw_value = "application/json"
        }
      ]
      body = <<EOF
      ({
          "text": "Scheduled update"
      })
      EOF
    }
  }
}

The properties of an executor with an schedule based trigger are defined as follows:

  • name: The name of the executor. The name field has the validation rule ^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$, and it does not allow capital letters.
  • workspace_id: The ID of the workspace that the executor namespace belongs to.
  • description: The description of the executor.
  • trigger: The type of trigger ex: schedule.
    • timezone: This refers to the specific time zone in which the job's scheduled times are interpreted and executed.
    • frequency: The intervals at which the job is scheduled to run.
  • operation:
    • webhook:
      • url: The URL of the API endpoint.
      • body:The payload or message to be included in the request.
      • headers: The headers to send with the webhook.
        • key: The key of the header.
        • raw_value: The raw value of the header.
        • secret_value: The secret value to load.
        • secret_name: The name of the secret.
        • vault_name: The name of the vault.

Learn more about executor properties in the Tailor Platform Provider documentation.

scheduledEventBasedExecutor: executor.#Executor & {
	Name:         "name"
	Description:  "description"
	Trigger: executor.#TriggerSchedule & {
		Timezone:  "UTC"
		Frequency: "* * * * *"
	}
	Target: executor.#TargetWebhook & {
		URL: common.#Script & {
			Expr: "\"http://localhost\""
		}
		Secret: secretmanager.#SecretValue & {
			VaultName: "vault"
			SecretKey: "key"
		}
	}
}

The properties of an executor with an schedule based trigger are defined as follows:

  • Name: The name of the executor. The name field has the validation rule ^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$, and it does not allow capital letters.
  • Description: The description of the executor.
  • Trigger: The type of trigger ex: #TriggerSchedule.
  • Timezone: This refers to the specific time zone in which the job's scheduled times are interpreted and executed.
  • Frequency: The intervals at which the job is scheduled to run.
  • Target:
  • URL: The URL of the API endpoint.
  • Body:The payload or message to be included in the request.
  • Secret: Secret is the key to generate tailor-signature-256 signature in your header. You need to use secret manager to store the secret key.
    • VaultName: The name of the vault.
    • SecretKey: The key that stores the secret.

Incoming Webhook Trigger

The Incoming Webhook Trigger enables external services to invoke an execution flow by sending an HTTP request to an exposed endpoint.

Payload Format

The payload structure received by the Executor's Incoming Webhook will have the following format:

{
  "args": {
    "method": "POST", // The HTTP method (e.g., POST, GET)
    "headers": {
      "x-name": "example", // Headers are available in lowercase with hyphens
      "x-array-elements": ["element1", "element2"] // header can be array
    },
    "body": {
      "name": "John",
      "full_name": {"first_name": "John","last_name": "Doe"}
    }
  }
}

The Executor's incoming webhook supports both application/json and application/x-www-form-urlencoded content types. When using form-urlencoded, form data is parsed into the body object with single values as strings and multiple values as arrays.

Here's an example of how to access different parts of the webhook payload:

Variables: common.#Script & {
    Expr: """
    ({
        "name": args.headers["x-name"], // lowercase, hyphen-value
        "first_name": args.body.full_name.first_name,
    })"""
}

When receiving form-urlencoded data, you can access the values directly from the body object:

Variables: common.#Script & {
    Expr: """
    ({
        "name": args.headers["x-name"], // lowercase, hyphen-value
        "title": args.body.title, // access form field directly
    })"""
}

1. Target type: tailor_graphql

In the following Terraform configuration, the tailor_executor resource defines an executor that listens for incoming webhook requests. When triggered, it executes a GraphQL mutation to create a new product.

executor_incoming_webhook.tf

resource "tailor_executor" "incoming_webhook_based_executor" {
  workspace_id = tailor_workspace.ims.id
  name         = "incoming-webhook-based-executor"
  description  = "exposes an endpoint"

  trigger = {
    webhook = {}
  }

  operation = {
	tailor_graphql = {
	  app_name = tailor_application.ims.name
	  invoker = {
		event_user = true
	  }
	  query     = <<EOF
	    mutation createProduct($title: String!) {
		  createProduct(input: {title: $title}) {
		    id
		  }
	    }
	  EOF
	  variables = <<EOF
	  ({
		"title": args.body.title,
		"headers": args.headers
	  })
	  EOF
	}
  }
}

The properties of an executor with an incoming webhook trigger are defined as follows:

  • name: The name of the executor. The name field has the validation rule ^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$, and it does not allow capital letters.
  • workspace_id: The ID of the workspace that the executor namespace belongs to.
  • description: The description of the executor.
  • trigger: The type of trigger ex: webhook.
    • webhook:
      • url: The computed URL for the webhook trigger.
  • operation:
    • tailor_graphql
      • app_name: The name of the app.
      • query: The query to be executed.
      • variables: The input variables for the query.
      • invoker: The user role may be required to execute the query.

For comprehensive information on executor properties, check the Tailor Platform Provider documentation.

incomingWebhookBasedExecutor: executor.#Executor & {
	Name:    "executor-01"
	Trigger: executor.#TriggerIncomingWebhook
	Target: executor.#TargetTailorGraphql & {
		AppName: "my-app"
		Invoker: settings.adminInvoker
		Query: """
			mutation createProduct($title: String!) {
				createProduct(input: { title: $title }) {
					id
				}
			}"""
		Variables: common.#Script & {
			Expr: """
			({
				"title": args.body.title,
				"name": args.headers["x-name"],
			})"""
		}
	}
}

The properties of an executor with an incoming webhook trigger are defined as follows:

  • Name: The name of the executor. The name field has the validation rule ^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$, and it does not allow capital letters.
  • Description: The description of the executor.
  • Trigger: The type of trigger ex: #TriggerIncomingWebhook.
  • Target:
  • TargetTailorGraphql
    • AppName: The name of the app.
    • Query: The query to be executed.
    • Variables: The input variables for the query.
    • Invoker: The user role may be required to execute the query.

2. Target type: job_function

Job functions provide asynchronous execution of JavaScript/TypeScript code for background processing and long-running tasks.

In the following example, a webhook trigger executes a job function asynchronously:

executor_job_function.tf

resource "tailor_executor" "webhook_function" {
  workspace_id = tailor_workspace.starwars.id
  name         = "webhook-function-01"
  description  = "Create a replica of a Species record"

  trigger = {
    webhook = {}
  }

  operation = {
    job_function = {
      name      = "webhookHi"
      script    = file("${path.module}/scripts/sample_function.js")
      variables = <<EOF
        ({
           "message": "hi " + (new Date()).toISOString()
        }) 
      EOF
    }
  }
}

The properties of an executor with a job function operation are defined as follows:

  • name: The name of the executor. The name field has the validation rule ^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$, and it does not allow capital letters.
  • workspace_id: The ID of the workspace that the executor namespace belongs to.
  • description: The description of the executor.
  • trigger: The type of trigger (webhook, event, or schedule).
  • operation:
    • job_function
      • name (Required): The name of the function.
      • script (Required): The script to execute.
      • invoker (Optional): The invoker of the operation.
      • variables (Optional): The variables to pass to the function.

For more details on writing functions, refer to the Function Service documentation.

#webhookJobFunction: executor.#Executor & {
  Name:        "webhook-function-01"
  Description: "Create a replica of a Species record"
  Trigger:     executor.#TriggerIncomingWebhook
  Target: executor.#TargetJobFunction & {
    Name:       "webhookHi"
    ScriptPath: "script/sample_function.js"
    Variables: common.#Script & {
      Expr: """
      ({
         "message": "hi " + (new Date()).toISOString()
      })
      """
    }
  }
}

The properties of an executor with a job function target are defined as follows:

  • Name: The name of the executor. The name field has the validation rule ^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$, and it does not allow capital letters.
  • Description: The description of the executor.
  • Trigger: The type of trigger (e.g., #TriggerIncomingWebhook).
  • Target:
    • TargetJobFunction
      • Name (Required): The name of the function.
      • ScriptPath (Required): The path to the script file.
      • Invoker (Optional): The invoker of the operation.
      • Variables (Optional): The variables to pass to the function.

For more details on writing functions, refer to the Function Service documentation.