This page applies to Apigee and Apigee hybrid.
View Apigee Edge documentation.
What
The ExternalCallout policy enables you to send gRPC requests to your gRPC server to implement custom behavior that isn't supported by Apigee policies. In your server's code, you can easily access and modify flow variables within a proxy's flow.
Apigee communicates with a gRPC server through an ExternalCallout policy via an API. Apigee uses the API to send flow variables to the gRPC server. Within your gRPC server, you can read—and depending on the variable, modify— the flow variables listed in the Flow variables reference page, as well as additional variables you specify within the policy's XML.
If you configure the gRPC server with Apigee and include this policy in a proxy, Apigee will handle API requests as follows:
- Apigee sends a message containing the flow variables to your gRPC server.
- Your gRPC server code executes, accessing and modifying variables as defined in the code. The gRPC server then sends a response containing all the flow variables back to Apigee.
- Apigee reads the response from your gRPC server. If any variables are added or modifiable flow variables are modified, they are updated in Apigee.
This policy is a Standard policy and can be deployed to any environment type. For information on policy types and availability with each environment type, see Policy types.
To learn more about sending gRPC requests, see the following links:
<ExternalCallout>
Defines an ExternalCallout policy.
<ExternalCallout async="true" continueOnError="true" enabled="true" name="EC">
This element has the following attributes that are common to all policies:
Attribute | Default | Required? | Description |
---|---|---|---|
name |
N/A | Required |
The internal name of the policy. The value of the Optionally, use the |
continueOnError |
false | Optional | Set to false to return an error when a policy fails. This is expected behavior for
most policies. Set to true to have flow execution continue even after a policy
fails. See also:
|
enabled |
true | Optional | Set to true to enforce the policy. Set to false to turn off the
policy. The policy will not be enforced even if it remains attached to a flow. |
async |
false | Deprecated | This attribute is deprecated. |
The following table describes the child elements of
<ExternalCallout>
.
Child Element | Required | Description |
---|---|---|
<TimeoutMs> |
Required | The request timeout in milliseconds for gRPC requests. |
<GrpcConnection> |
Required | Specifies the name of an existing TargetServer to be the gRPC server to
send requests to.
|
<Configurations> |
Optional | Allows you to configure various aspects of the ExternalCallout policy,
including the <Property> and <FlowVariable>
elements. |
Example 1
Working examples of ExternalCallout are available at External Callout Samples on GitHub.
The following example illustrates an ExternalCallout policy configuration.
<ExternalCallout enabled="true" continueOnError="false" name="ExternalCallout-1"> <DisplayName>External Callout 1</DisplayName> <TimeoutMs>5000</TimeoutMs> <GrpcConnection> <Server name="external-target-server"/> </GrpcConnection> <Configurations> <Property name="with.request.content">true</Property> <Property name="with.request.headers">false</Property> <Property name="with.response.content">true</Property> <Property name="with.response.headers">false</Property> <FlowVariable>example1.flow.variable</FlowVariable> <FlowVariable>example2.flow.variable</FlowVariable> </Configurations> <ExternalCallout>
The example sends a request to an external gRPC server represented by the
TargetServer named external-target-server
, with the following configurations:
<Property>
: Include request and response content, but not the request and response headers, in the request sent to the gRPC server.<FlowVariable>
: Include additional flow variablesexample1.flow.variable
andexample2.flow.variable
, specified by theFlowVariable
elements, in the request sent to the gRPC server.
Example 2
In the following example, the useTargetUrl
attribute of the Audience
element is set to true
. When useTargetUrl
is true
,
the hostname of the gRPC target server is used as the audience. For
example, if the host of the server is my-grpc-server-java.a.run.app
, then the audience
used will be https://my-grpc-server-java.a.run.app
.
<ExternalCallout continueOnError="false" enabled="true" name="External-Callout-1"> <DisplayName>External-Callout-1</DisplayName> <GrpcConnection> <Server name="cloud_run_server_name"/> <Authentication> <GoogleIDToken> <Audience useTargetUrl="true"/> </GoogleIDToken> </Authentication> </GrpcConnection> <TimeoutMs>5000</TimeoutMs> <Configurations> <Property name="with.request.content">true</Property> <Property name="with.request.headers">true</Property> <Property name="with.response.content">true</Property> <Property name="with.response.headers">true</Property> <FlowVariable>example.flow.variable</FlowVariable> <FlowVariable>another.flow.variable</FlowVariable> </Configurations> </ExternalCallout>
Child element reference
The following sections describe the child elements of ExternalCallout
.
<TimeoutMs>
The request timeout in milliseconds for gRPC requests. <TimeoutMs>
must be
a positive number.
<GrpcConnection>
The <GrpcConnection>
element sets the gRPC server to be an existing
TargetServer
, specified by the name
attribute. See the
TargetServer
resource reference page.
Note: The
protocol for the TargetServer
must be GRPC
.
For example, the following code
<GrpcConnection> <Server name="external-target-server"/> </GrpcConnection>
specifies the gRPC server to be an existing TargetServer
named
external-target-server
.
Use the <Authentication>
element (described later in this section) to generate a Google-issued
OpenID Connect token
to make authenticated calls to gRPC-based services, such as custom services hosted in
Cloud Run.
The following table describes the child elements of
<GrpcConnection>
.
Child Element | Required? | Description |
---|---|---|
<Server> element |
Required | Specifies the gRPC server. |
<Authentication> element |
Optional | Generates a Google-issued OpenID Connect token to make authenticated calls to gRPC-based services, such as Cloud Run. |
<Server>
element
Specifies the gRPC server.
The following table describes the attributes of the <Server>
element.
Attribute | Description | Default | Presence | Type |
---|---|---|---|---|
name |
The name of an existing |
N/A | Required | String |
<Authentication>
element
Generates a Google-issued OpenID Connect token to make authenticated calls to gRPC-based services, such as custom services hosted in Cloud Run. Use of this element requires setup and deployment steps described in Using Google authentication. With proper setup, the policy creates an authentication token for you and adds it to the service request.
This element has one required child element: GoogleIDToken
.
Default | N/A |
Required? | Optional. |
Type | Complex type |
Parent Element | <GrpcConnection> |
Child Elements |
<GoogleIDToken> |
The Authentication
element uses the following syntax:
Syntax
<ExternalCallout> ... <GrpcConnection> <Server name="cloud_run_server_name"/> <Authentication> <HeaderName ref="FLOW_VARIABLE">STRING</HeaderName> <GoogleIDToken> <Audience ref="variable-1">STRING</Audience> <IncludeEmail ref="variable-2">BOOLEAN</IncludeEmail> </GoogleIDToken> </Authentication> </GrpcConnection> </ExternalCallout>
Example
The following example shows the GoogleIDToken
element:
<ExternalCallout continueOnError="false" enabled="true" name="External-Callout-1"> <DisplayName>External-Callout-1</DisplayName> <GrpcConnection> <Server name="cloud_run_server_name"/> <Authentication> <HeaderName ref='my-variable'>X-Serverless-Authorization</HeaderName> <GoogleIDToken> <Audience>https://cloudrun-hostname.a.run.app</Audience> </GoogleIDToken> </Authentication> </GrpcConnection> <TimeoutMs>5000</TimeoutMs> <Configurations> <Property name="with.request.content">true</Property> <Property name="with.request.headers">true</Property> <Property name="with.response.content">true</Property> <Property name="with.response.headers">true</Property> <FlowVariable>example.flow.variable</FlowVariable> <FlowVariable>another.flow.variable</FlowVariable> </Configurations> </ExternalCallout>
Attributes
None.
<HeaderName> child element
By default, when an Authentication configuration is present, Apigee generates a bearer
token and injects it into the Authorization
header in the message sent to the target system.
The HeaderName
element allows you to specify the name of a different header
to hold that bearer token. This feature is particularly useful when the target is a Cloud Run
service that uses the X-Serverless-Authorization
header. The Authorization
header,
if present, is left unmodified and also sent in the request.
Default | N/A |
Required? | No |
Type | String |
Parent Element | <Authentication> |
Child Elements | None |
The HeaderName
element uses the following syntax:
Syntax
<ExternalCallout> ... <Authentication> <HeaderName ref="FLOW_VARIABLE">STRING</HeaderName> <GoogleIDToken> ... </GoogleIDToken> </Authentication> ... </ExternalCallout>
With static string
In this example, the generated bearer token is added, by default, to a header named X-Serverless-Authorization
that is sent to the target system. The Authorization
header,
if present, is left unmodified and also sent in the request.
<Authentication> <HeaderName>X-Serverless-Authorization</HeaderName> <GoogleIDToken> <Audience>https://cloudrun-hostname.a.run.app</Audience> </GoogleIDToken> </Authentication>
With variable reference
In this example, the generated bearer token is added, by default, to a header named X-Serverless-Authorization
that is sent to the target system. If my-variable
has a value, that value is used
instead of the default string. The Authorization
header,
if present, is left unmodified and also sent in the request.
<Authentication> <HeaderName ref='my-variable'>X-Serverless-Authorization</HeaderName> <GoogleIDToken> <Audience>https://cloudrun-hostname.a.run.app</Audience> </GoogleIDToken> </Authentication>
<GoogleIDToken> child element
Generates Google-issued OpenID Connect tokens to make authenticated calls to Google services, such as custom services hosted in Cloud Run.
Default | N/A |
Required? | Required |
Type | String |
Parent Element | <Authentication> |
Child Elements | <Audience> <IncludeEmail> |
The GoogleIDToken
element uses the following syntax:
Syntax
<ExternalCallout> ... <GrpcConnection> <Server name="cloud_run_server_name"/> <Authentication> <GoogleIDToken> <Audience ref="context-variable" useTargetUrl='BOOLEAN'>STRING</Audience> <IncludeEmail ref="context-variable">BOOLEAN</IncludeEmail> </GoogleIDToken> </Authentication> </GrpcConnection> </ExternalCallout>
Example
The following example shows the GoogleIDToken
element:
<Authentication> <GoogleIDToken> <Audience>https://httpserver0-bar.run.app</Audience> <IncludeEmail>true</IncludeEmail> </GoogleIDToken> </Authentication>
<Audience> child element
The audience for the generated authentication token, such as the API or account that the token grants access to.
If the value of Audience
is empty, the ref
is empty or resolves to an
empty value, and useTargetUrl
is true
, then "https://" +
(hostname of the gRPC target server) is used as the audience.
For example, if the host of the server is my-grpc-server-java.a.run.app
, then the
audience used will be https://my-grpc-server-java.a.run.app
.
By default, useTargetUrl
is false
.
<Audience>explicit-audience-value-here</Audience> or: <Audience ref='variable-name-here'/> or: <Audience ref='variable-name-here' useTargetUrl='true'/> or: <Audience useTargetUrl='true'/>
Default | N/A |
Required? | Required |
Type | String |
Parent Element | <GoogleIDToken> |
Child Elements | None. |
<IncludeEmail> child element
If set to true
, the generated authentication token will contain the
service account email
and email_verified
claims.
Default | false |
Required? | Optional |
Type | Boolean |
Parent Element | <GoogleIDToken> |
Child Elements | None. |
<Configurations>
The <Configurations>
element allows you to configure various aspects of
the ExternalCallout policy,
including <Property>
and <FlowVariable>
.
The following table describes the child elements of
<Configurations>
.
Child Element | Required? | Description |
---|---|---|
<Property> |
Required | Specifies whether request/response headers and/or content will be sent to
the server. Possible values are |
<FlowVariable> |
Required | Specifies what additional flow variables should be sent to the server. |
<Property>
The <Property>
element specifies whether request/response
headers and/or content will be sent to the server. Possible values are true
(the item will be sent) or false
(the item won't be sent). The default value
is false
.
The following table describes the attributes of the <Property>
element.
Attribute | Description | Default | Presence | Type |
---|---|---|---|---|
name |
Specifies what content will be sent to
the server. Possible values for
|
N/A | Required | String |
<FlowVariable>
The <FlowVariable>
element specifies what additional flow variables
will be sent to the server. The value of
<FlowVariable>
is a prefix of a variable, rather than the full variable name.
For example , if the a.b.c
, the
value of a variable named a.b.c
will be sent to the server. Similarly, the value
of a variable
named a.b.c.my-variable
will be sent to the server. But the value of a variable
named a.x.another-variable
will not be sent, because it does not have the
prefix a.b.c
. Here are some examples
<Configurations> <FlowVariable>a.b.c</FlowVariable> <FlowVariable>d.e.f</FlowVariable> </Configurations>
Error Reference
Deployment errors
Error name | Cause |
---|---|
FAILED_PRECONDITION |
This error happens if the service account is missing when the proxy is
configured with the <Authentication> tag.
For example: Deployment of \"organizations/foo/apis/apiproxy/revisions/1\" requires a service account identity, but one was not provided with the request. |
PERMISSION_DENIED |
This error happens if there is a permission problem with the service account if
the proxy is
configured with the <Authentication> tag. Possible causes:
|
Runtime errors
The table below describes runtime errors, which can occur when the policy executes.
Fault code | HTTP Status | Cause |
---|---|---|
GrpcTlsInitFailed |
500 |
This error will occur if there are any issues with initializing TLS with the gRPC server (such as Keystore or truststore issues). |
steps.externalcallout.[error_code] |
500 |
|
steps.externalcallout.ExecutionError |
500 |
This error will occur if any other exception occurs during the execution of this policy. The underlying exception will be exposed in the faultstring. If there was an issue with the credentials to the gRPC server, the error will look something like this: { "fault": { "faultstring": "Encountered the following exception while sending the gRPC request or processing the response: [io.grpc.StatusRuntimeException: UNAVAILABLE: Credentials failed to obtain metadata].", "detail": { "errorcode": "steps.externalcallout.ExecutionError" } } } You can look at the logs of the MP for further debugging pointers. |
googletoken.EmptyIDTokenAudience |
500 |
|
steps.externalcallout.ExecutionError
with fault string:
|
500 |
This error happens if the API proxy is configured with the
|
steps.externalcallout.ExecutionError with faultstring containing
PERMISSION DENIED .
For example, the faultstring will look like the following for Cloud Run:
|
500 |
This error happens if the API proxy is configured with the
|
Miscellaneous errors
The table below describes miscellaneous errors. See the cause for more details.
Fault code | Cause |
---|---|
ReferencesExistToGrpcServer |
This error will occur if a user tries to delete a gRPC target server, but the server is still being used by other policies. |
Faults
The fault variables in the following table are set for all policies by default. See Variables specific to policy errors.
Variables | Where | Examples |
---|---|---|
fault.name="fault_name" |
fault_name is the name of the fault, as listed in the Runtime errors
table above. The fault name is the last part of the fault code. |
fault.name matches "ExecutionError". |
externalcallout.[policy_name].failed |
policy_name is the user-specified name of the policy that threw the fault. |
externalcallout.ExternalCallout-1.failed = true |