Skip to main content

Api Mapper

Api mapper is created to support highly customizable search. You can configure api mapper and use it to automate making requests to some endpoint and modifying based on returned body provided entity.

Create api Mapper

  1. Go to Quasiris Search Cloud
  2. Type 'Api Mapper' in the search box from the top left corner
  3. Click on the result
  4. Enter your name and code
  5. Click 'Create'

Use cases

  1. Use it as search filter
  2. Use it for api calling (2 request are maid at the end)
# qsc type
base_url="http://localhost:8082"
tenantCode="playground"
apiMapperCode="apiMapperCode"
searchCode="mysearchCode"
curl -XPOST "$base_url/api/v1/api-mapper/map/$tenantCode/$apiMapperCode" -d '
{
"type": "qsc",
"request": {
"url": "'"$base_url/api/v1/search/$tenantCode/$searchCode"'",
"method": "GET"
}
"searchResultId": "'"$searchCode"'"
}
'

# default type
curl -XPOST "$base_url/api/v1/api-mapper/map/$tenantCode/$apiMapperCode" -d '
{
"type": "default",
"request": {
"url": "http://localhost/some/different/path",
"method": "GET"
}
}
'

Structure

In general structure of api mapper looks like below:

{
"code": "api-mapper-code",
"name": "api-mapper-name",
"type": "api-mapper",
"requestMapping": {
"type": "qsc|templated",
"templated": {
"engine": "freemarker",
"template": "{\"url\":\"http://localhost:8082/mypath\",\"method\":\"POST\",\"body\":{\"requestBodyKey\":\"request Body value\"},\"auth\":{\"type\":\"oauth2\",\"oauth2\":{\"clientId\":\"myClientId\",\"clientSecret\":\"myClientSecret\",\"issuerUri\":\"http://localhost:8083/issuer-uri\"}},\"headers\":[\"Authorization: Basic xxxxxxx\"]}"
},
"qsc": {
"url": "http://localhost:8082/mypath",
"method": "POST",
"bodyTemplate": "{\"requestBodyKey\":\"request Body value\"}",
"auth": {
"type": "oauth2",
"oauth2": {
"clientId": "myClientId",
"clientSecret": "myClientSecret",
"issuerUri": "http://localhost:8083/issuer-uri"
}
},
"headers": [
"Authorization: Basic xxxxxxx"
]
}
},
"responseMapping": {
"type": "qsc|templated",
"templated": {
"engine": "freemarker|javascript",
"template": "{\"${apiResp.myKey}\":\"${apiResp.myValue}\",\"${bodyToHandle.myKey}\":\"${bodyToHandle.myValue}\"}"
},
"qsc": {
"jsonPathMappings": [
{
"get": [
"$.apiResp.informationWhichWouldBeInserted"
],
"thenPut": [
{
"root": "$",
"key": "keyToInsert"
}
],
"ignoreErrors": true
}
]
}
},
"securityIds": [
"basicAuthConfig",
"apiKeyAuthConfig"
]
}

Request mapping

requestMapping describes how the api call to external resource must be executed. You can specify it in different ways, changing type option (default type is qsc)

Qsc type

{
"requestMapping": {
"type": "qsc",
"qsc": {
"url": "http://localhost:8082/mypath",
"method": "POST",
"bodyTemplate": "{\"requestBodyKey\":\"request Body value\"}",
"auth": {
"type": "oauth2",
"oauth2": {
"clientId": "myClientId",
"clientSecret": "myClientSecret",
"issuerUri": "http://localhost:8083/issuer-uri"
}
},
"headers": [
"Authorization: Basic xxxxxxx"
]
}
}
}
  • url
  • method
  • bodyTemplate - template using freemarker language
  • auth - authentication. Used only for advanced authentication. At that moment we only support oauth2 with client credentials flow (with issuer uri). if you want to use different authentication method, use headers instead
  • headers - headers in HeaderName: HeaderValue format
Body template variables

For bodyTemplate property you can specify dynamic template based on specific variables, which differ between use cases.

  1. Qsc filter use case
    • uuid - random uuid for this request
    • inputBody - SearchResult object
    • inputVars.parameters - parameters of the request (json object)
  2. Api call
    • uuid - random uuid for this request
    • inputBody - SearchResult in case of qsc type when you make a request, any value if you use default type

Template examples:

  1. Example for qsc filter use case
<@compress single_line=true>
{
"requestId": "${uuid}",
"docids": [
<#list inputBody.documents as doc>
${doc.document.docId}<#sep>,</#sep>
</#list>
],
"requestParameter": "${inputVars.parameters['query.q']}"
}
</@compress>
  1. Example for api call use case
<@compress single_line=true>
{
"requestId": "${uuid}",
"docids": [
<#list inputBody.documents as doc>
${doc.document.docId}<#sep>,</#sep>
</#list>
],
}
</@compress>

Proxy Type

{
"type": "api-mapper",
"requestMapping": {
"type": "proxy",
"proxy": {
"url": "http://localhost:8082/mypath",
"timeout": 4000,
"headers": [
"Authorization: Basic xxxxxxx"
]
}
}
}
  • url - the endpoint address to be mapped
  • headers - headers in HeaderName: HeaderValue format

Templated Type

The idea of templated request is to be able to control in a dynamic way all options (url, method, clientId, clientSecret etc.)

{
"requestMapping": {
"type": "templated",
"templated": {
"engine": "freemarker",
"template": "{\"url\":\"${inputBody.context.baseUrl}/mypath?${inputBody.context}\",\"method\":\"${inputBody.context.method}\",\"bodyTemplate\":\"{\\\"requestBodyKey\\\":\\\"request Body value\\\"}\",\"auth\":{\"type\":\"oauth2\",\"oauth2\":{\"clientId\":\"myClientId\",\"clientSecret\":\"${inputBody.context.clientSecret}\",\"issuerUri\":\"http://localhost:8083/issuer-uri\"}},\"headers\":[\"Authorization: Basic xxxxxxx\"]}"
}
}
}

More readable template:

{
"url": "${inputBody.context.baseUrl}/mypath?${inputBody.context}",
"method": "${inputBody.context.method}",
"bodyTemplate": "{\"requestBodyKey\":\"request Body value\"}",
"auth": {
"type": "oauth2",
"oauth2": {
"clientId": "myClientId",
"clientSecret": "${inputBody.context.clientSecret}",
"issuerUri": "http://localhost:8083/issuer-uri"
}
},
"headers": [
"Authorization: Basic xxxxxxx"
]
}

Available variables are the same as for qsc type

Response mapping

The last step is to take result of api call and change source body based on it. Response mapping describes this.

Qsc type

When we use qsc type for response mapping, to update SearchResult the application will iterate through the all documents and execute all jsonPathMappings for each of them

Let's assume that initial search result documents were:

[
{
"id": "someValue1",
"document": {
"docId": "1",
"name": "product1"
}
},
{
"id": "someValue2",
"document": {
"docId": "2",
"name": "product2"
}
},
{
"id": "someValue3",
"document": {
"docId": "3",
"name": "product3"
}
}
]

and the response from api mapper was:

[
{
"id": "1",
"dynamicInfo": "The product is not available during July"
},
{
"id": "2",
"dynamicInfo": "The product is available"
},
{
"id": "3",
"dynamicInfo": "The product is available only in combination with product 1"
}
]

and we want to have dynamicInfo in the SearchResult. In that case we would configure responseMapping like below:

{
"responseMapping": {
"type": "qsc",
"qsc": {
"jsonPathMappings": [
{
"get": [
"$.apiResp[?(@.id == $.document.docId)]",
"$.result[0].dynamicInfo"
],
"thenPut": [
{
"root": "$",
"key": "dynamicInfo"
}
],
"ignoreErrors": false
}
]
}
}
}

At the result SearchResult must be:

[
{
"id": "someValue1",
"document": {
"docId": "1",
"name": "product1",
"dynamicInfo": "The product is not available during July"
}
},
{
"id": "someValue2",
"document": {
"docId": "2",
"name": "product2",
"dynamicInfo": "The product is available"
}
},
{
"id": "someValue3",
"document": {
"docId": "3",
"name": "product3",
"dynamicInfo": "The product is available only in combination with product 1"
}
}
]
Json mappings
{
"responseMapping": {
"type": "qsc",
"qsc": {
"jsonPathMappings": [
{
"get": [
"$.apiResp[?(@.id == $.document.docId)]",
"$.result[0].dynamicInfo"
],
"thenPut": [
{
"root": "$",
"key": "dynamicInfo"
}
],
"ignoreErrors": false
}
]
}
}
}

The idea of json mapping is to configure the changes using JsonPath library

We use get section to retrieve a value from apiResp. And then we use thenPut section to put retrieved value to document. Because JsonPath has some limitations sometimes we need to use multiple json paths to retrieve value.

  • get section. To retrieve the value. The value will be used in thenPut section

    Variables:

    • document - current document
    • apiResp - full api response
    • result - intermediate result (it is null in the first line, is it the result of previous lines calculation in other lines)
  • thenPut section. To put the value into current document

    Properties:

    • root prefix of the document when we want to put the value (can't be anything except json object)
    • key json key where to put value
  • ignoreErrors to ignore errors for this specific json mapping (if we have error we stop processing next documents)

Templated type

The idea of templated type is to construct raw response in a flexible way

Freemarker engine

Available variables:

  • apiResp - response from api
  • bodyToHandle - full body which we want to change (SearchResult in case of qsc filter type)