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
- Go to Quasiris Search Cloud
- Type 'Api Mapper' in the search box from the top left corner
- Click on the result
- Enter your name and code
- Click 'Create'
Use cases
- Use it as search filter
- 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"
]
}
}
}
urlmethodbodyTemplate- template usingfreemarkerlanguageauth- 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, useheadersinsteadheaders- headers inHeaderName: HeaderValueformat
Body template variables
For bodyTemplate property you can specify dynamic template based on specific variables, which differ between use
cases.
- Qsc filter use case
uuid- random uuid for this requestinputBody-SearchResultobjectinputVars.parameters- parameters of the request (json object)
- Api call
uuid- random uuid for this requestinputBody-SearchResultin case ofqsctype when you make a request, any value if you usedefaulttype
Template examples:
- 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>
- 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 mappedheaders- headers inHeaderName: HeaderValueformat
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.
-
getsection. To retrieve the value. The value will be used inthenPutsectionVariables:
document- current documentapiResp- full api responseresult- intermediate result (it is null in the first line, is it the result of previous lines calculation in other lines)
-
thenPutsection. To put the value into current documentProperties:
rootprefix of the document when we want to put the value (can't be anything except json object)keyjson key where to put value
-
ignoreErrorsto 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 apibodyToHandle- full body which we want to change (SearchResultin case of qsc filter type)