Dynamic API Ownership Assignment using GraphQL

Prev Next

In your application ecosystem, it is essential to know who owns what API. The number of APIs in an application can vary across areas, such as teams and regions, making manual ownership assignment tedious and unscalable.

Dynamic API Ownership Assignment in Traceable addresses this scalability issue by automating API ownership mapping based on spans it observes while receiving traffic. Using GraphQL, you can dynamically allocate ownership data to APIs.


What will you learn in this Topic?

By the end of this topic, you will understand:

  • What Dynamic API Ownership is and how it helps

  • How does Dynamic Ownership differ from Manual Ownership Assignment

  • Prerequisites before setting up dynamic API ownership

  • How to configure API ownership assignment policies using GraphQL

  • How to verify the assigned ownerships

  • How to manage API ownership policies


What is Dynamic API Ownership and How does it Help?

Dynamic API Ownership is the process of assigning API owners automatically based on spans that Traceable observes. Instead of manually assigning owners to APIs, you can set up policies using span metadata such as user.email and user.team.

Dynamic assignment of API ownership helps you in the following manner:

  • Makes API ownership scalable.

  • Reduces errors and maintenance overhead.

  • Enables precise and granular ownership assignment based on span data and regular expressions.


Dynamic Ownership v/s Manual Ownership Assignment

The following table compares dynamic and manual ownership assignment based on various aspects:

Aspect

Dynamic Ownership

Manual Ownership

Assignment Method

Automatically assigned based on span attributes.

Manually assigned by Account Owners or team members with the relevant permissions.

Scalability

Highly scalable as created policies apply to all matching APIs.

Low scalability, as the effort increases with the number of APIs.

Accuracy

Consistent across APIs.

Prone to errors due to an increased API count.

Maintenance Overhead

Low maintenance, as ownership gets automatically updated with incoming traffic.

High maintenance, as it requires manual updates.

Use-Case

Ideal for dynamic environments where API counts increase and ownership can change with time.

Ideal for static environments where API counts and ownership remain the same at all times.


Before you begin

Before you set up the dynamic API ownership policies, you must remember the following:

  • This feature is available for setup only via the GraphQL API.

  • Setting up a policy requires JWT-based authentication using an Authorization: Bearer <JWT_Token> header.

  • Updates in API ownership may take up to 24 hours to reflect in the platform after the dynamic assignment.

  • References for span attributes that you can use for setting up a policy are available under Analytics → Explorer → Spans tab → Results section → click any row item → Attributes tab.


Setting up Dynamic Ownership

You can set up API ownership by creating GraphQL policies using the createPolicy mutation. Traceable provides the following methods for dynamic assignment:

  • Static Assignment — Assign a constant owner based on a specific filter or condition. If the condition matches, Traceable assigns the API to the owner you specify in the policy.

  • Dynamic Assignment — Assign the owner based on the availability of a specific span attribute. If the attribute exists, Traceable sets the span attribute value as the API owner.

  • Regex-based Dynamic Assignment — Assign the owner based on an attribute and a regular expression you specify. If the attribute exists, Traceable extracts the required value and sets it as the API owner.

The following sections discuss the above methods using examples.

Static Assignment

The following example assigns a constant owner (Test Owner) for all APIs matching the following conditions:

  • The service name is Test Service.

  • The status code is between 200 and 400.

mutation createPolicyToStaticicallyAssignApiOwner {
  createPolicy(
    createPolicyRequest: {
      name: "Set API owner to Test Owner if the service name is Test Service"
      source: { type: SPAN }
      filter: {
        type: LOGICAL
        policyLogicalFilter: {
          logicalOperator: AND
          policyFilters: [
            {
              type: RELATIONAL
              policyRelationalFilter: {
                operator: GREATER_THAN_OR_EQUALS
                lhsType: EXPRESSION
                rhsType: VALUE
                lhsExpression: {
                  type: FIELD
                  field: { location: DATA, key: "stringAttribute", lookupKeys: ["http.status_code"] }   # The attribute (lookupKeys) that Traceable should look for, in the spans
                }
                rhsConstant: "200"   # The value that the above lookupKeys attribute should have
              }
            }
            {
              type: RELATIONAL
              policyRelationalFilter: {
                operator: LESS_THAN_OR_EQUALS
                lhsType: EXPRESSION
                rhsType: VALUE
                lhsExpression: {
                  type: FIELD
                  field: { location: DATA, key: "stringAttribute", lookupKeys: ["http.status_code"] }   # The attribute (lookupKeys) that Traceable should look for, in the spans
                }
                rhsConstant: "400"   # The value that the above lookupKeys attribute should have
              }
            }
            {
              type: RELATIONAL
              policyRelationalFilter: {
                operator: EQUALS
                lhsType: EXPRESSION
                rhsType: VALUE
                lhsExpression: {
                  type: FIELD
                  field: { location: DATA, key: "stringAttribute", lookupKeys: ["servicename"] }   # The attribute (lookupKeys) that Traceable should look for, in the spans
                }
                rhsConstant: "Test Service"   # The value that the above lookupKeys attribute should have
              }
            }
          ]
        }
      }
      actions: [
        {
          type: ENTITY_UPDATE,
          entityUpdate: {
            entityType: "API",
            entityUpdates: [
              {
                attributeKey: "API.owner",   # The attribute Traceable should assign ownership to
                valueExpression: {
                  type: CONSTANT,
                  constant: "Test Owner"   # The value Traceable should assign to the above attribute
                }
              }
            ]
          }
        }
      ]
      disabled: false
    }
  ) {
    id
  }
}

Dynamic Assignment

The following example assigns a dynamic owner based on the value of the kubernetes.labels.owner span attribute, if available.

mutation createPolicyToDynamicallyAssignApiOwner {
  createPolicy(
    createPolicyRequest: {
      name: "Set API owner dynamically from span attribute"
      source: { type: SPAN }
      filter: {
        type: LOGICAL
        policyLogicalFilter: {
          logicalOperator: NOT
          policyFilters: [
              {
                type: UNARY
                policyUnaryFilter: {
                  type: EXPRESSION
                  expression: {
                    type: FIELD
                    field: { location: DATA, key: "stringAttribute", lookupKeys: ["kubernetes.labels.owner"] }   # The attribute (lookupKeys) Traceable should look for, in the spans
                  }
                  operator: IS_MISSING
                }
              }
          ]
        }
      }
      actions: [
        {
          type: ENTITY_UPDATE,
          entityUpdate: {
            entityType: "API",
            entityUpdates: [
              {
                attributeKey: "API.owner",   # The attribute Traceable should assign ownership to
                valueExpression: {
                  type: FIELD
                  field: { location: DATA, key: "stringAttribute", lookupKeys: ["kubernetes.labels.owner"] }   # The span attribute (lookupKeys) whose value Traceable should extract and assign to the above attribute key
                }
              }
            ]
          }
        }
      ]
      disabled: false
    }
  ) {
    id
  }
}

Regex-based Dynamic Assignment

The following example assigns a dynamic owner based on the username it extracts from the email present in the kubernetes.label.owner span attribute, if available. For example, if the kubernetes.label.owner attribute has testowner@traceable.ai as the email, the regex extracts testowner and assigns it to the API.

mutation createPolicyToExtractAndDynamicallyAssignApiOwner {
  createPolicy(
    createPolicyRequest: {
      name: "Set API owner dynamically from span attribute"
      source: { type: SPAN }
      filter: {
        type: LOGICAL
        policyLogicalFilter: {
          logicalOperator: NOT
          policyFilters: [
            {
              type: UNARY
              policyUnaryFilter: {
                type: EXPRESSION
                expression: {
                  type: FIELD
                  field: { location: DATA, key: "stringAttribute", lookupKeys: ["kubernetes.labels.owner"] }   # The attribute (lookupKeys) Traceable should look for, in the spans
                }
                operator: IS_MISSING
              }
            }
          ]
        }
      }
      actions: [
        {
          type: ENTITY_UPDATE,
          entityUpdate: {
            entityType: "API",
            entityUpdates: [
              {
                attributeKey: "API.owner",   # The attribute Traceable should assign ownership to
                valueExpression: {
                  type: FUNCTION
                  function: {
                    name: REGEX
                    expressions: []
                    regex: {
                      input: {
                        type: FIELD
                        field: { location: DATA, key: "stringAttribute", lookupKeys: ["kubernetes.labels.owner"] }     # The attribute (lookupKeys) whose value you wish to extract and modify
                      }
                      pattern: "([^@]+)(@.+)",   # The RE2 regex pattern Traceable should use to extract the requried value
                      captureGroupIndex: 1   # The parenthesis in the above pattern from which Traceable should extract the value
                      fallbackIfNotMatched: {
                        type: CONSTANT
                        constant: ""  
                      }
                    }
                  }
                }
              }
            ]
          }
        }
      ]
      disabled: false
    }
  ) {
    id
  }
}

Verifying Ownership Assignment

Once you have created a policy, ownership may take up to 24 hours to be reflected in the Traceable platform. To verify ownership assignment, you can:

  1. Navigate to Catalog → Inventory and click the API endpoint on which you wish to verify the assignment.

  2. View the assigned owner under the API Ownership section in the Overview tab. The owner is assigned based on the policy you created.


Managing API Ownership

As your application ecosystem evolves, you may wish to modify the ownership of APIs based on the updated environment and teams. To do that, you can view the existing policies in your application and update them according to your requirements.

Step 1 — View Existing Policies

Execute the following GraphQL query that lists all API ownership policies that you may have created:

query getPolicies {
  policies(filter: { policyActionTypes: ENTITY_UPDATE }) {
    count
    total
    results {
      id
      name
      disabled
      actions {
        type
        entityUpdate {
          entityType
          entityUpdates {
            attributeKey
            valueExpression {
              type
              field {
                location
                key
                lookupKeys
              }
              constant
            }
          }
        }
      }
      filter {
        type
        policyLogicalFilter {
          logicalOperator
          policyFilters {
            type
            policyLogicalFilter {
              logicalOperator
              policyFilters {
                type
                policyRelationalFilter {
                  operator
                  lhsType
                  rhsType
                  lhsExpression {
                    type
                    field {
                      location
                      key
                      lookupKeys
                    }
                  }
                  rhsField {
                    location
                    key
                    lookupKeys
                  }
                  rhsConstant
                }
                policyArrayFilter {
                  type
                  arrayOperator
                  policyRelationalFilter {
                    operator
                    lhsType
                    rhsType
                    lhsExpression {
                      type
                      field {
                        location
                        key
                        lookupKeys
                      }
                    }
                    rhsField {
                      location
                      key
                      lookupKeys
                    }
                    rhsConstant
                  }
                }
              }
            }
            policyRelationalFilter {
              operator
              lhsType
              rhsType
              lhsExpression {
                type
                field {
                  location
                  key
                  lookupKeys
                }
              }
              rhsField {
                location
                key
                lookupKeys
              }
              rhsConstant
            }
            policyArrayFilter {
              type
              arrayOperator
              policyRelationalFilter {
                operator
                lhsType
                rhsType
                lhsExpression {
                  type
                  field {
                    location
                    key
                    lookupKeys
                  }
                }
                rhsField {
                  location
                  key
                  lookupKeys
                }
                rhsConstant
              }
            }
          }
        }
      }
      policyScope {
        scopeType
        environmentIds
      }
      source {
        policyEventType
      }
    }
  }
}

Step 2 — Update an Existing Policy

Once you have viewed the list of policies, you can update a policy to do the following:

  • Refine existing filters or add new ones

  • Update the API ownership logic

To do these, execute the following GraphQL query:

mutation updatePolicy {
  updatePolicy(
    updatePolicyRequest: {
      policy: {
        id: "<policy_id>"   # The ID corresponding to the policy you wish to update. To get this ID, you must execute the query in the above section
        name: "New policy name"   # The updated policy name
        source: { type: SPAN }
        filter: {
          type: LOGICAL
          policyLogicalFilter: {
            logicalOperator: AND
            policyFilters: [
              {
                type: RELATIONAL
                policyRelationalFilter: {
                  operator: GREATER_THAN_OR_EQUALS
                  lhsType: EXPRESSION
                  rhsType: VALUE
                  lhsExpression: {
                    type: FIELD
                    field: { location: DATA, key: "stringAttribute", lookupKeys: ["http.status_code"] }   # The updated attribute (lookupKeys) that Traceable should look for, in the spans
                  }
                  rhsConstant: "200"   # The value that the above lookupKeys attribute should have
                }
              }
              {
                type: RELATIONAL
                policyRelationalFilter: {
                  operator: LESS_THAN_OR_EQUALS
                  lhsType: EXPRESSION
                  rhsType: VALUE
                  lhsExpression: {
                    type: FIELD
                    field: { location: DATA, key: "stringAttribute", lookupKeys: ["http.status_code"] }   # The updated attribute (lookupKeys) that Traceable should look for, in the spans
                  }
                  rhsConstant: "400"   # The value that the above lookupKeys attribute should have
                }
              }
              {
                type: RELATIONAL
                policyRelationalFilter: {
                  operator: EQUALS
                  lhsType: EXPRESSION
                  rhsType: VALUE
                  lhsExpression: {
                    type: FIELD
                    field: { location: DATA, key: "stringAttribute", lookupKeys: ["servicename"] }   # The updated attribute (lookupKeys) that Traceable should look for, in the spans
                  }
                  rhsConstant: "Test Service"   # The value that the above lookupKeys attribute should have
                }
              }
            ]
          }
        }
        actions: [
          {
            type: ENTITY_UPDATE,
            entityUpdate: {
              entityType: "API",
              entityUpdates: [
                {
                  attributeKey: "API.owner",   # The updated attribute Traceable should assign ownership to
                  valueExpression: {
                    type: CONSTANT,
                    constant: "Test Owner"   # The updated value Traceable should assign to the above attribute
                  }
                }
              ]
            }
          }
        ]
        disabled: false
      }
    }
  ) {
    id
  }
}