Skip to content

Web Services Before/After Operation Rule

Warning

When creating custom rules, verify that any log events that contain sensitive data are removed. If you add custom log events for testing while creating custom rules, they may expose sensitive data in the log files.

The Web Services connector uses the following operation rules:

You can create Before and After Operation rules through the Create Connector Rule API, as well as update them through the Update Connector Rule API.

Standard Template for the Before Operation Rule
{
    "description": "The Web Services connector will call this rule before performing ANY defined operation.\nThis rule can be used to add/update values in the endpoint object before performing the operation and/or add persistent values to the application's data.",
    "type": "WebServiceBeforeOperationRule",
    "signature": {
        "input": [
            {
                "name": "application",
                "description": "The application associated with the operation being processed.",
                "type": null
            },
            {
                "name": "requestEndPoint",
                "description": "The current request information containing the header, body, context url, method type, response attribute map, and response code. \nThis object can be modified directly and returned by the rule to update the endpoint information \n that is used by the current operation",
                "type": null
            },
            {
                "name": "oldResponseMap",
                "description": "earlier response object ",
                "type": null
            },
            {
                "name": "restClient",
                "description": "REST Client Object",
                "type": null
            },
            {
                "name": "provisioningPlan",
                "description": "A ProvisioningPlan object containing the payload of the http request. A provisioning plan has an account request which defines the operation to be performed on the account. \n An account request can contain multiple attribute requests and each attribute request represents an operation on a single account attribute.",
                "type": null
            },
            {
                "name": "partition",
                "description": "If applicable, a Partition object with the current aggregation's partitioning information.",
                "type": null
            }
        ],
        "output": {
            "name": "Map",
            "description": "An updated or unmodified 'requestEndPoint' object. If application object modifications are desired, create a map containing keys 'updatedEndPoint' and 'connectorStateMap' and use it as the return value; \n Within the new map, the 'updatedEndPoint' can be set to an updated or unmodified 'requestEndPoint' object. \nThe 'connectorStateMap' will be saved as persistent values in the application definition.",
            "type": null
        }
    },
    "sourceCode": {
        "version": "1.0",
        "script": "// source code"
    },
    "attributes": null,
    "id": "2b9280857b252a16017130d8a7756f24",
    "name": "Before Operation Rule Template for Web Services Connector",
    "created": "2021-08-10T15:57:58.005Z",
    "modified": "2023-01-30T10:47:42.124Z"
}

Note

You need to add the beforeRule as explained in SailPoint's Rule Guide to use the Web Service Before Operation Rule.

Standard Template for the After Operation Rule
{
    "description": "The Web Services connector will call this rule after performing ANY defined operation. \nThis rule can be used to update the parsed resource object and/or add persistent values to the application's data. ",
    "type": "WebServiceAfterOperationRule",
    "signature": {
        "input": [
            {
                "name": "application",
                "description": "The application whose data file is being processed.",
                "type": null
            },
            {
                "name": "requestEndPoint",
                "description": "The current request information contain header, body ,response object",
                "type": null
            },
            {
                "name": "processedResponseObject",
                "description": "Response Object processed by the Web services connector",
                "type": null
            },
            {
                "name": "rawResponseObject",
                "description": "Response Object returned from the end system",
                "type": null
            },
            {
                "name": "restClient",
                "description": "REST Client Object",
                "type": null
            }
        ],
        "output": {
            "name": "Map",
            "description": "A newly created map containing a 'data' key and optionally, a 'connectorStateMap' key.  The 'data' value should be set to a map containing a parsed list of accounts/groups.\nThe 'connectorStateMap' value can be set to a map containing values that will be persisted to the application definition.",
            "type": null
        }
    },
    "sourceCode": {
        "version": "1.0",
        "script": "\nimport sailpoint.object.ResourceObject;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap; "
    },
    "attributes": null,
    "id": "2c9180857b252a16017b30c8e75c6e23",
    "name": "After Operation Rule Template for Web Services Connector",
    "created": "2021-08-10T15:57:57.980Z",
    "modified": "2021-08-31T08:14:12.647Z"
}

Note

You need to add the afterRule as explained in SailPoint's Rule Guide to use the Web Service After Operation Rule.

Before Operation Rule

The Before Operation Rule is used to calculate attributes before a web service operation call. This rule is used by the Web Services connector before performing operations such as Test Connection and Aggregation.

Note

Refer to Java docs for more information.

Input Objects

The following table lists the input objects for WebServiceBeforeOperationRule:

Object Type Description
application sailpoint.object.Application The application whose data file is being processed.
requestEndPoint sailpoint.connector.webservices.EndPoint Current request information. It contains the header, body, context URL, method type, response attribute map, successful response code.
oldResponseMap java.util.Map The response object returned from earlier endpoint configuration of the same operation type, such as Account Aggregation, Get Object, etc.
restClient sailpoint.connector.webservices.WebServicesClient A Web Services Client (HTTP Client) object that enables you to call the Web Services API target system.
provisioningPlan sailpoint.object.ProvisioningPlan Used to update the payload of the HTTP request. A provisioning plan has an account request which defines the operation to be performed on the account. An account request can contain multiple attribute requests, and each attribute request represents an operation on a single account attribute. This argument enables the user to update the body/payload or URL attributes of the endpoint object using the provisioningPlan information.

Return Objects

The Web Services Before Operation Rule uses the following return objects:

sailpoint.connector.webservices.EndPoint
sailpoint.connector.webservices.Map

The rule allows the user to return the endpoint object (requestEndPoint) or a map. The map can hold updatedEndPoint and connectorStateMap keys, where the value expected is the Endpoint (requestEndPoint) and connectorStateMap objects, respectively. The connectorStateMap object is a map that contains the key and value of the attribute that must be updated in the application through the Before Operation Rule.

Examples of the Before Operation Rule

The following example displays the rule used by the Web Services connector before performing operations such as Test Connection and Aggregation.

Before Operation Rule Example
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE Rule PUBLIC "sailpoint.dtd" "sailpoint.dtd">
<Rule name="Example Rule" type="WebServiceBeforeOperationRule">
    <Description>
        This rule is used by the  Web Services connector before performing any operation like testconnection, aggregation etc.
    </Description>
    <Source><![CDATA[
        import com.google.gson.Gson;
        import sailpoint.tools.Util;
        Map obj = (Map) application.getAttributeValue("transientValues");
        if(null != obj) {
            String cursor = obj.get("cursor");
            if(Util.isNotNullOrEmpty(cursor)) {
                Map postBodyMap = (Map) requestEndPoint.getBody();
                postBodyMap.put("jsonBody", "{\"cursor\":\"" + cursor + "\"}");
                requestEndPoint.setFullUrl(requestEndPoint.getFullUrl() + "/continue");
            }
        }
        return requestEndPoint;

    ]]></Source>
</Rule>

The following example is for the provisioningPlan rule:

Before Operation Rule Example for provisioningPlan
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import connector.common.JsonUtil;
import connector.common.Util;
import sailpoint.connector.webservices.EndPoint;
import sailpoint.connector.webservices.WebServicesClient;
import sailpoint.object.Application;
import sailpoint.object.ProvisioningPlan;
import sailpoint.object.ProvisioningPlan.AccountRequest;

    Map body = requestEndPoint.getBody();
    String jsonBody = (String) body.get("jsonBody");
    log.info("Rule - Modify Body: running");

    try {

        Map jsonMap = JsonUtil.toMap(jsonBody);
        if (jsonMap != null) {
            Object roleEntry = jsonMap.get("webSiteAndRole");
            String role = "";
            if (roleEntry != null && roleEntry instanceof ArrayList) {
                ArrayList rolesArray = (ArrayList) roleEntry;
                if (rolesArray.size() > 0) {
                    role = (String) rolesArray.get(0);
                }
            } else if (roleEntry != null) {
                role = (String) roleEntry;
            }

            jsonMap.remove("webSiteAndRole");
            jsonMap.put("webSiteAndRole", role);

            log.info("Rule - Modify Body: setting webSiteAndRole = " + role);
            String webID = "";
            if (provisioningPlan != null) {
                log.info("Rule - Modify Body: plan is not null");
                for (AccountRequest accReq : Util.iterate(provisioningPlan.getAccountRequests())) {
                    log.info("Rule - Modify Body: iterating over account requests");
                    for (ProvisioningPlan.AttributeRequest attReq : Util.iterate(accReq.getAttributeRequests())) {
                        log.info("Rule - Modify Body: iterating over attribute requests");
                        String attrName = attReq.getName();
                        String value = null;
                        if (attrName != null && "webId".equalsIgnoreCase(attrName)) {
                            webID = (String) attReq.getValue();
                            log.info("Rule - Modify Body: setting webID = " + webID);
                    }
                }
            }
        } else {
            log.info("Rule - Modify Body: plan is null");
        }

        if (!"".equals(webID)) {
            jsonMap.put("webID", webID);
        }

// Add in any other missing fields
        if (!jsonMap.containsKey("webLogonEmail")) {
            jsonMap.put("webLogonEmail", "");
        }

        if (!jsonMap.containsKey("taxID")) {
            jsonMap.put("taxID", "");
        }

        if (!jsonMap.containsKey("taxIdType")) {
            jsonMap.put("taxIdType", "");
        }

        if (!jsonMap.containsKey("actorLogonId")) {
            jsonMap.put("actorLogonId", "");
        }

        String finalBody = JsonUtil.render(jsonMap);
        body.put("jsonBody", finalBody);
        requestEndPoint.setBody(body);
    }
} catch (Exception ex) {
    log.error("Rule - Modify Body: " + ex);
}

return requestEndPoint;

The following is an example of the Web Services connector executing the Before Operation Rule on a target system API:

Before Operation Rule Example Executed on a Target System API
import connector.common.JsonUtil;
import sailpoint.connector.webservices.WebServicesClient;
    try {
        WebServicesClient client = new WebServicesClient();
        String url = "http://hostname:portnumber/RestFormDataWS/beforeRuleMock";
        Map args = new HashMap();
        args.put(WebServicesClient.ARG_URL, url);
        client.configure(args);
        Map header = new HashMap();
        // Information can be fetched from requestEndpoint and updated in the header and body
        header.put("Authorization","XXXX XXXX");
        header.put("Content-Type","application/json");

        List<String> allowedStatuses = new ArrayList();
        allowedStatuses.add("2**");

        Map payload = new HashMap();
        payload.put("jsonBody","{\"user\":{\"userId\":\"RinkuSharma\",\"firstName\":\"Rinku\",\"lastName\":\"Sharma\",\"title\":\"manager\"}}");

        String response = client.executePost(url, payload, header, allowedStatuses);
        // if response contains token it can be updated in the requestEndpoint header or body
        // the requestEndpoint will be used for execution of the particular operation configured
        log.info("response: " + response);
    } catch (Exception e) {
        log.error(e.getMessage(), e);
    }

The following example displays the Before Operation Rule being used to update the values for endpoint and application attributes before execution of the endpoint:

Map updatedInfoMap = new HashMap();
    requestEndPoint.setFullUrl(requestEndPoint.getFullUrl().replaceAll("&&", "&"));
    Map connectorStateMap = new HashMap();
    connectorStateMap.put("accesstoken","Bearer accessTokenGeneratedInBeforeRuleScript");
    updatedInfoMap.put("updatedEndPoint",requestEndPoint);
    updatedInfoMap.put("connectorStateMap",connectorStateMap);
    return updatedInfoMap;

After Operation Rule

The After Operation Rule is used to calculate attributes after a web service operation call.

It is used by the Web Services connector to update parsed resource objects and save connectorStateMap values. The list of objects converts to Resource objects and the connectorStateMap values are updated in the application object permanently. The returned map holds the data key for the resource object list and the connectorStateMap key for updated attributes that must be saved in the application.

Note

Refer to Java docs for more information.

Input Objects

Object Type Description
application sailpoint.object.Application Application whose data file is being processed.
requestEndPoint sailpoint.connector.webservices.EndPoint Current request information. It contains the header, body, context URL, method type, response attribute map, successful response code.
processedResponseObject List<Map<String, Object>> This object is a list of map objects for accounts and groups. The map objects contain keys as identity attributes for the application schema, and the associated values are all the account and group attributes passed by the connector after parsing the respective API response.
rawResponseObject String String object which holds the raw response returned from the target system, which can be in JSON or XML form.
restClient sailpoint.connector.webservices.WebServicesClient A Web Services Client (HTTP Client) object that enables you to call the Web Services API target system.

Return Objects

java.util.Map

The Map object returned from the After Operation Rule may contain any or all of the following:

  • Updated list of account/group resource objects; identified by key data
  • Attribute values to be updated into the application via connector state map; identified by key connectorStateMap

Each resource (account/group) object is a Map-type object which contains key-value pairs, where the key represents the schema attribute name and the value represents the account/group attribute value.

Examples for After Operation Rules

The following example displays the After Operation Rule used to update accounts, groups, and application attribute values after execution of the endpoint:

After Operation Rule Example for Updating Accounts, Groups, and Application Attributes
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Iterator;

//We can parse the response(rawResponseObject) to fetch from the respective executed Endpoint operation.
Map updatedMapInfo = new HashMap();
if (processedResponseObject != null){

    for (Map iterateMap : processedResponseObject) {
        if (iterateMap != null ) {
            Set keySet = iterateMap.keySet();
            for (String s : keySet) {
                if (s.equals("given_name")) {
            String forStr = (String) iterateMap.get("given_name");
            forStr = "TEST"+ forStr;
                iterateMap.put("given_name", forStr);
        }
            }
        }
    }
updatedMapInfo.put("data", processedResponseObject);
}
Map connectorStateMap = new HashMap();
connectorStateMap.put("refresh_token","refreshTokenGeneratedInAfterRuleScript");
updatedInfoMap.put("connectorStateMap",connectorStateMap);
return updatedMapInfo;

The following example displays the After Operation Rule being used by the Web Services connector to update the resource object:

After Operation Rule Example for Updating the Resource Object
<![CDATA[
    import connector.common.JsonUtil;
    import java.util.HashMap;
    import java.util.Map.Entry;
    import java.util.Map;
    import java.util.List;
    import java.util.ArrayList;
    import javax.net.ssl.HttpsURLConnection;
    import java.io.BufferedReader;
    import java.io.PrintStream;
    import java.io.StringWriter;
    import java.text.SimpleDateFormat;
    import sailpoint.tools.GeneralException;

Map updatedMapInfo = new HashMap();
List list = new ArrayList();
ArrayList<String> Roles = new ArrayList<String>();
Map response = (Map) JsonUtil.toMap(rawResponseObject);
int RoleSize = 0;
String newName;
List Finallist = new ArrayList();
List workspace = new ArrayList();

log.error("RULEWS response at start" + response);
if (response.get("data") != null) {

    list = (ArrayList) response.get("data");

    for(int d = 0; d < list.size(); d++ ){

            Map responseMap = (Map) list.get(d);

            if (responseMap.get("attributes") != null) {
                Map newmap = new HashMap();
                Map data = (Map) responseMap.get("attributes");
                newmap.put("firstName", data.get("firstName"));
                newmap.put("lastName",data.get("lastName"));
                newmap.put("displayName",data.get("displayName"));
                newmap.put("userName",data.get("userName"));
                newmap.put("email",data.get("email"));

            if (data.get("workspaceMemberships") != null) {
                ArrayList Workspacedetail = (ArrayList) data.get("workspaceMemberships");
                for (int i = 0; i < Workspacedetail.size(); i++) {
                    Map work = (Map) Workspacedetail.get(i);
                    for (int w = 0; w < work.size(); w++) {
                        if (work.get("workspaceName") != null) {
                            workspace.add(work.get("workspaceName"));
                                Roles = (ArrayList) work.get("workspaceRoles");
                                for (int r = 0; r < Roles.size(); r++) {
                                    if (Roles.get(r) != null) {
                                        newName = Roles.get(r).toString() + " - " + work.get("workspaceName");
                                        if(newName != null) {
                                            Roles.set(r, newName);
                                            newmap.put("workspaceRoles", Roles);
                                        }
                                    }
                                }
                        }
                        break;
                    }
                }
            }

            Finallist.add(newmap);

        }
    }

}
log.error("RULEWS newmap at end" + newmap);
log.error("RULEWS Finallist at end" + Finallist);

log.error("RULEWS processedResponseObject Before is " + processedResponseObject);

updatedMapInfo.put("data", Finallist);
log.error("RULEWS updatedMapInfo is " + updatedMapInfo);

return updatedMapInfo;
log.error("RULEWS processedResponseObject after is " + processedResponseObject);

]]

Web Services Classes Used in Before and After Operation Rules

This section describes the following types of Web Services classes:

  • WebServicesClient
  • EndPoint class

Note

The Web Services classes mentioned in this section are general guidelines. New classes and methods may be added.

WebServicesClient

Constructor

Signature Description
public WebServicesClient() Default constructor.
public WebServicesClient(java.util.Map args) throws java.lang.Exception Constructor that configures the client using the given args.

Methods

Method Description
public void configure(java.util.Map args) throws java.lang.Exception Configure connection parameters. See the ARG_* constants.
public java.lang.String executeGet(java.util.Map headers, java.util.List<java.lang.String> allowedStatuses) throws java.lang.Exception Execute method GET with headers. Parameters: headers (Request headers), allowedStatuses (Allowed status codes).
public java.lang.String executeGet(java.lang.String url, java.util.Map headers, java.util.List<java.lang.String> allowedStatuses) throws java.lang.Exception Execute method GET with URL and headers. Parameters: url (Request URL), headers (Request headers), allowedStatuses (Allowed status codes). Returns a Response object.
public java.lang.String executePost(java.lang.String url, java.lang.Object payload, java.util.Map headers, java.util.List<java.lang.String> allowedStatuses) throws java.lang.Exception Execute method POST with URL, payload, and headers. Parameters: url, payload (Request body), headers, allowedStatuses.
public java.lang.String executePost(java.lang.String url, java.lang.Object payload, java.util.List<java.lang.String> allowedStatuses) throws java.lang.Exception Execute method POST with URL and payload. Parameters: url, payload, allowedStatuses. Returns a Response object.
public java.lang.String executePut(java.lang.String url, java.lang.Object payload, java.util.List<java.lang.String> allowedStatuses) throws java.lang.Exception Execute method PUT with URL and payload. Parameters: url, payload, allowedStatuses.
public java.lang.String executePut(java.lang.String url, java.lang.Object payload, java.util.Map headers, java.util.List<java.lang.String> allowedStatuses) throws java.lang.Exception Execute method PUT with URL, payload, and headers. Returns a Response object.
public java.lang.String executePatch(java.lang.String url, java.lang.Object payload, java.util.List<java.lang.String> allowedStatuses) throws java.lang.Exception Execute method PATCH with URL and payload.
public java.lang.String executePatch(java.lang.String url, java.lang.Object payload, java.util.Map headers, java.util.List<java.lang.String> allowedStatuses) throws java.lang.Exception Execute method PATCH with URL, payload, and headers. Returns a Response object.
public java.util.Map<java.lang.String,java.lang.String> getResponseHeaders() Get last executed request's response headers.
public java.lang.String executeDelete(java.lang.String url, java.util.List<java.lang.String> allowedStatuses) throws java.lang.Exception Execute method DELETE with URL. Parameters: url, allowedStatuses.
public java.lang.String executeDelete(java.lang.String url, java.util.Map headers, java.util.List<java.lang.String> allowedStatuses) throws java.lang.Exception Execute method DELETE with URL and headers. Returns a Response object.

EndPoint Class

Constructor

Signature Description
public EndPoint() Sets the EndPoint.

Methods

Method Description
public void setAfterRule(java.lang.String value) Sets the after rule name.
public void setBeforeRule(java.lang.String value) Sets the before rule name.
public void setParseRule(java.lang.String value) Sets the parsing rule.
public void setHttpMethodType(java.lang.String value) Sets the HTTP method (PUT, POST, GET, PATCH, and DELETE) for the particular operation (Create User, Update User, Account Aggregation, etc.).
public void setOperationType(java.lang.String value) Sets the operation (Account Aggregation, Group Aggregation, Create Account) for the particular operation record.
public void setRootPath(java.lang.String value) Sets the root of the JSON response returned from the managed system for the particular operation.
public void setBaseUrl(java.lang.String value) Sets the base URL (the machine ID or IP and the port where the service is executing) for the operation that needs to be performed. Ideally this should be common for all the operations.
public void setContextUrl(java.lang.String value) Sets the context URL for the particular operation.
public void setFullUrl(java.lang.String value) Sets the complete URL (endpoint) of the operation that needs to be performed for the particular operation.
public void setSequenceNumberForEndpoint(int value) Sets the sequence number for the particular operation.
public void setUniqueNameForEndPoint(java.lang.String value) Sets a unique operation name for the particular operation.
public void setResMappingObj(java.util.Map value) Sets the response mapping for the response attribute returned in the JSON response from the managed system for the particular operation. The key is the attribute name (attribute in the schema) and the value is the JSON response path after the root path.
public void setHeader(java.util.Map value) Sets HTTP header information in the form of key-value pairs (for example, key='ContentType' Value='Application/JSON').
public void addHeader(java.lang.String entry, java.lang.String value) Adds a key-value pair if the header exists, or creates a header and adds it.
public void setBody(java.util.Map value) Sets the HTTP body information as a Map. The map contains three keys: bodyFormat, bodyFormData, and jsonBody. The bodyFormat value can be raw, meaning the user has provided values as a raw JSON string, otherwise the user has provided values in key-value format that must be converted into JSON.
public void setResponseCode(java.util.List value) Sets the value of successful response codes as a list (200, 299, 201). If the connector receives any other response codes, it considers them request failures.
public java.lang.String getAfterRule() Fetches the name of the after rule assigned to the particular operation.
public java.lang.String getBeforeRule() Fetches the name of the before rule assigned to the particular operation.
public java.lang.String getHttpMethodType() Fetches the HTTP method type (GET, PUT, POST, DELETE, and PATCH) provided to the particular operation.
public java.lang.String getOperationType() Fetches the operation type (Account Aggregation, Create Account, Group Aggregation) provided to the particular operation.
public java.lang.String getRootPath() Fetches the root path provided to the particular operation.
public java.lang.String getBaseUrl() Fetches the base URL, which is common for all operations.
public java.lang.String getContextUrl() Fetches the context URL provided to the particular operation.
public java.lang.String getFullUrl() Fetches the full URL, which is a combination of the base URL and the context URL for the particular operation.
public int getSequenceNumberForEndpoint() Fetches the sequence number for the particular operation, which decides the priority of execution for an operation if there are multiple endpoints of the same operation type.
public java.lang.String getUniqueNameForEndPoint() Fetches the unique name provided to the particular operation.
public java.util.Map getResMappingObj() Fetches the response mapping map, which has the key as the schema attribute and the value as a JSON path in the JSON response.
public java.util.Map getHeader() Fetches the HTTP header map that holds the header information for the particular operation.
public java.util.Map getBody() Fetches the body map that holds the body information with keys such as bodyFormat, jsonBody, and bodyFormData. bodyFormat can have raw or formData values; bodyFormData is a map; jsonBody is a string with the whole JSON code.
public java.util.List getResponseCode() Fetches the success response code list values, which decide whether an operation was successful, for the particular operation.
public sailpoint.object.Attributes getAttributes() Fetches all attributes.
public java.lang.Object getAttribute(java.lang.String name) Fetches a single attribute.
public Boolean getBooleanAttributeValue(java.lang.String name) Fetches a Boolean value for a specific attribute.
public java.lang.String getStringAttributeValue(java.lang.String name) Fetches a string value for a specific attribute.
public void setAttribute(java.lang.String name, java.lang.Object value) Sets a specified attribute to a specified value.
public java.lang.String getPaginationSteps() Fetches the paging steps as a string which decides how account and group paging works.
public void setPaginationSteps(java.lang.String paginationSteps) Sets the paging steps as a string which decides how account and group paging works.
public java.lang.String toString() Overrides toString in the java.lang.Object class.
public java.lang.String getResponseBody() Retrieves the last executed request's response body.
public void setXpathNamespaces(Map<String, String> xpathNamespaces) Sets XPath namespaces using the supplied map object.
public Map<String, String> getXpathNamespaces() Retrieves XPath namespaces.
public int getPagingInitialOffset() Retrieves the initial page offset.
public void setPagingInitialOffset(int pagingInitialOffset) Sets the initial paging offset.
public int getPagingSize() Retrieves the page limit.
public void setPagingSize(int pagingLimit) Sets the page limit.

Documentation Feedback

Feedback is provided as an informational resource only and does not form part of SailPoint’s official product documentation. SailPoint does not warrant or make any guarantees about the feedback (including without limitation as to its accuracy, relevance, or reliability). All feedback is subject to the terms set forth at https://developer.sailpoint.com/discuss/tos.