Service Callout to another API Proxy with URL tag

Due to the business logic, we have a requirement to use the same Service Call Out Policy to hit internal API and Management Server depending on the environment.

So, we are using SAME Service Call Out policy to call resource of internal API Proxy and Management Server by environments.

We are using the <URL> tag to directly hit the target. This value of URL is taken from KVM, so that we can keep separate values in each environment. When we set the KVM value as Management Server URL, the call gets through. But when we set the KVM value as FQDN of Internal API Proxy, the service call out returns 404. However, when we hit the FQDN of Internal API Proxy though postman, it returns proper data.

Step to Reproduce the problem:

  • 1.Create a KVM entry called backendURL (under KVM called test) with value as api.enterprise.apigee.com
  • 2.Upload the 2 API proxies attached
  • 3.Hit the ‘{host}/testmediator’. Since there is no proper auth details, we would get 401 as expected. That means, the service callout works when calling any outside endpoints with <URL> tag.
  • 4.Now, change the KVM value to ‘{host}/testmediatorbackend and hit ‘{host}/testmediator’. Now we see that the service callout is returning 404 and the call even is not coming o TestMediatorBackend API Proxy. That means, when we are calling the API proxy which is within the same host with URL option, we are not able to hit the same.
  • 5.Now, hit the ‘{host}/testmediatorbackend’ from postman, it goes through. That means, there is no issue in calling this api proxy from a anywhere other than service call out.

This is blocking our complete work on revoke token functionality, as we are not able to hit management server and internal api proxy at the same time.

Any suggestion ?

Solved Solved
0 8 2,711
2 ACCEPTED SOLUTIONS

I think you have testmediator calling (sometimes) to testmediatorbackend.

Within testmediator, your ServiceCallout policy calls out to

<URL>https://{backendURL}/v1/organizations/{organization.name}/oauth2/revoke?enduser=1&app=2</URL>

But the testmediatorbackend API Proxy listens on /testmediatorbackend .

It does not listen on /v1

That would explain why you see 404, wouldn't it?

View solution in original post

Thanks @dino for the pointers.

Although I was not able to get the last option working, I have managed to make it work with the following service callout policy (I read backendURLSuffix and backendHost from KVM)

<ServiceCallout async="false" continueOnError="false" enabled="true" name="SC-ToBackend">
    <Request>
        <Set>
            <Headers>
                <Header name="Authorization">{request.header.Authorization}</Header>
            </Headers>
            <Verb>POST</Verb>
            <Path>{backendURLSuffix}/organizations/{organization.name}/oauth2/revoke</Path>
            <QueryParams>
                <QueryParam name="enduser">{endUserId}</QueryParam>
                <QueryParam name="app">{appId}</QueryParam>
            </QueryParams>
        </Set>
    </Request>
    <Response>revokeTokenResponse</Response>
    <Timeout>100</Timeout>
    <HTTPTargetConnection>
        <URL>https://{backendHost}</URL>
    </HTTPTargetConnection>
</ServiceCallout>

View solution in original post

8 REPLIES 8

Is your Apigee installation Edge Saas, or are you managing the installation yourself on your own machines?

hit it from postman, it goes through. That means, there is no issue in calling this api proxy from a anywhere other than service call out.

Demonstrating success from postman means.... your desktop that runs postman has access to the target. A similar ServiceCallout will fail, if the machine on which the ServiceCallout runs, does not have access to the target.

You said the target is "an internal API server". If you are using Edge SaaS, it would not be surprising if the message processor running in the public cloud does not have access to your internal API.

Hello @Dino,

We are on Public Cloud. As I have pointed out, the target is another Apigee API Proxy that is running in the same org and env, where the calling proxy is. Details can be found inside the API Proxies. Any help to fix this is highly appreciated.

Have you looked at using LocalTargetConnection ? What you describe is the reason this feature was created.

As I specified in the post, I have a requirement to use the same policy code to hit API in environment A and Management Service in environment B. I can't use LocalTargetConnection, as I need to reach out to Management Server in environment B

I think you have testmediator calling (sometimes) to testmediatorbackend.

Within testmediator, your ServiceCallout policy calls out to

<URL>https://{backendURL}/v1/organizations/{organization.name}/oauth2/revoke?enduser=1&app=2</URL>

But the testmediatorbackend API Proxy listens on /testmediatorbackend .

It does not listen on /v1

That would explain why you see 404, wouldn't it?

Not really. Here is what is present the api bundle TestMediator:

1. KVM has a parameter "backendURL" set to {host}+"/testmediatorbackend"

2. I read this parameter in context variable backendURL

3. In the call Out policy I do

<URL>https://{backendURL}/v1/organizations/{organization.name}/oauth2/revoke?enduser=1&app=2</URL>.

4. And as expected, I can see in trace that callout is happening to

5947-trace.png

which is

'https://{backendURL}/v1/organizations/{organization.name}/oauth2/revoke?enduser=1&app=2'

or

'https://{host}/testmediatorbackend/v1/organizations/{organization.name}/oauth2/revoke?enduser=1&app=2' (if we expand backendURL)

5. And my TestMediatorBackend API is listening to "https://{host}/testmediatorbackend" (base path is testmediatorbackend), which is same as point 4.

ah yes, you have tripped over a very frustrating and confusing behavior in the ServiceCallout. It is not possible to simply "build the string" for the URL in the way you have done. You can replace {backendURL} with a pure hostname, but it cannot contain a slash, or the ServiceCallout will not behave properly. Despite what the trace shows, the policy is not doing what you think it should.

For a related question on this topic, see this Q&A.

The way to do it for you is to configure your serviceCallout like this:

<ServiceCallout name="SC-ToBackend-Alternative">
    <Request>
        <Set>
            <Headers>
                <Header name="Authorization">Basic abcd</Header>
            </Headers>
            <Verb>POST</Verb>
            <Path>{servicecallout_path}</Path>
        </Set>
    </Request>
    <Response>revokeTokenResponse</Response>
    <Timeout>60000</Timeout>
    <Response>calloutResponse</Response>
    <HTTPTargetConnection>
      <URL>https://this.will.be.changed</URL>
    </HTTPTargetConnection>
</ServiceCallout>

And then... prior to that step in your policy flow, use a different policy to set 2 variables - one for the host and one for the path. In the case you would like to use the "message template" idea for building the URL, you can do it in a JS step like this:

<Javascript name='JS-AssignServiceCalloutUrlVariables' timeLimit='200' >
  <Properties>
    <Property name='hostvar'>servicecallout.SC-ToBackend.target.url</Property>
    <Property name='pathvar'>servicecallout_path</Property>
    <Property name='urltemplate'>https://{backendURL}/v1/organizations/{organization.name}/oauth2/revoke?enduser=1&app=2</Property>
  </Properties>
  <IncludeURL>jsc://messageTemplate.js</IncludeURL>
  <ResourceURL>jsc://assignServiceCalloutUrlVariables.js</ResourceURL>
</Javascript>

The magic JS in assignServiceCalloutUrlVariables.js looks like this:

var urlvalue = (new MessageTemplate(properties.urltemplate)).fill();
var re = new RegExp('^(https?://[^/]+)(/.*)$');
var match = re.exec(urlvalue);
if (match) {
  context.setVariable(properties.hostvar, match[1]);
  context.setVariable(properties.pathvar, match[2]);
}

And the flow looks like this:

            <Step>
                <Name>KVM-ReadTargetUrl</Name>
            </Step>
            <Step>
              <Name>JS-AssignServiceCalloutUrlVariables</Name>
            </Step>
            <Step>
                <Name>SC-ToBackend</Name>
            </Step>

see attached for the working code.frontend.zip

Alternatively, you could read both HOST and PATH from KVM and set them both. In that case you would have something like this in the ServiceCallout:

<ServiceCallout name="SC-ToBackend">
    <Request>
        <Set>
            <Headers>
                <Header name="Authorization">Basic abcd</Header>
            </Headers>
            <Verb>POST</Verb>
        </Set>
    </Request>
    <Response>revokeTokenResponse</Response>
    <Timeout>60000</Timeout>
    <Response>calloutResponse</Response>
    <HTTPTargetConnection>
      <URL>https://{backendHost}/{backendPathAndQuery}</URL>
    </HTTPTargetConnection>
</ServiceCallout>

Thanks @dino for the pointers.

Although I was not able to get the last option working, I have managed to make it work with the following service callout policy (I read backendURLSuffix and backendHost from KVM)

<ServiceCallout async="false" continueOnError="false" enabled="true" name="SC-ToBackend">
    <Request>
        <Set>
            <Headers>
                <Header name="Authorization">{request.header.Authorization}</Header>
            </Headers>
            <Verb>POST</Verb>
            <Path>{backendURLSuffix}/organizations/{organization.name}/oauth2/revoke</Path>
            <QueryParams>
                <QueryParam name="enduser">{endUserId}</QueryParam>
                <QueryParam name="app">{appId}</QueryParam>
            </QueryParams>
        </Set>
    </Request>
    <Response>revokeTokenResponse</Response>
    <Timeout>100</Timeout>
    <HTTPTargetConnection>
        <URL>https://{backendHost}</URL>
    </HTTPTargetConnection>
</ServiceCallout>