Class DatadogProvisioningTargetNativeSync

java.lang.Object
edu.internet2.middleware.grouper.app.provisioning.GrouperProvisioningTargetNativeSync
edu.internet2.middleware.grouper.app.datadog.DatadogProvisioningTargetNativeSync

public class DatadogProvisioningTargetNativeSync extends GrouperProvisioningTargetNativeSync
Datadog-specific GrouperProvisioningTargetNativeSync: builds native target reporting beans for sync-back to the generic grouper_prov_group / grouper_prov_user tables.

Both groups and users are captured from the raw Datadog JSON (JSON Pointer paths, like SCIM/Adobe), hooked at the API-commands seam (DatadogApiCommands.retrieveRoles/ retrieveTeams/retrieveUsers/retrieveUserByEmail/getRoleUsers) where the full JSON node is in scope. This avoids losing any Datadog field that the DatadogGroup / DatadogUser typed beans do not model; operators can capture any JSON field via nativeAttributesGroups / nativeAttributesEntities with a name and optional JSON-Pointer path.

Datadog speaks JSON:API, so every object arrives as an envelope { "id": ..., "type": ..., "attributes": { ... } }. The capture seam hands this envelope node straight through, so the default/operator pointers are nested under /attributes (e.g. /attributes/name, /attributes/email) and the target id is the top-level /id -- exactly where DatadogUser.fromJson(com.fasterxml.jackson.databind.JsonNode)/DatadogGroup.fromJson(com.fasterxml.jackson.databind.JsonNode) read them.

groupType ("role" vs "team") is the one default that is not in the Datadog response JSON -- the commands/DAO set it on the typed bean programmatically based on which endpoint produced the object. To preserve the old behavior (the typed-bean capture wrote groupType), the commands first overlay the known group type onto a shallow copy of the envelope via nodeWithGroupType(JsonNode, String), so the default pointer /attributes/groupType resolves. This mirrors the merged-JSON capture used by the Google connector (which assembles a group from two reads before capturing).

Memberships are NOT captured from JSON here, but they ARE captured on the WRITE path as well as the read path. On read, the team-membership and role-user beans are recorded by the DAO via captureTeamMemberships(java.lang.String, java.util.List<edu.internet2.middleware.grouper.app.datadog.DatadogMembership>) / captureRoleMemberships(java.lang.String, java.util.List<edu.internet2.middleware.grouper.app.datadog.DatadogUser>) (those typed-bean helpers are unchanged). On write, DatadogTargetDao.insertMembership/deleteMembership record the edge directly into the native membership mirror via captureMembershipInsertFromCurrentProvisioner(java.lang.String, java.lang.String) / captureMembershipDeleteFromCurrentProvisioner(java.lang.String, java.lang.String) (-> recordTargetNativeMembershipInsert/ recordTargetNativeMembershipDelete), like Adobe/SCIM. So a membership add/remove is recorded into the mirror on the write and converges on the write pass; only the group/user OBJECT attributes still capture on the read path.

  • Constructor Details

    • DatadogProvisioningTargetNativeSync

      public DatadogProvisioningTargetNativeSync()
  • Method Details

    • getDefaultNativeAttributeConfigsEntities

      protected List<GrouperProvisioningNativeAttributeConfig> getDefaultNativeAttributeConfigsEntities()
      Description copied from class: GrouperProvisioningTargetNativeSync
      Per-protocol sensible-default attribute list for entities (users) when the operator hasn't configured nativeAttributesEntities. Override in protocol subclasses to return a curated list (e.g. SCIM core schema fields). Default is empty — for LDAP this means "no extra capture beyond what the regular target query returned," which is the historical behavior.
      Overrides:
      getDefaultNativeAttributeConfigsEntities in class GrouperProvisioningTargetNativeSync
    • getDefaultNativeAttributeConfigsGroups

      protected List<GrouperProvisioningNativeAttributeConfig> getDefaultNativeAttributeConfigsGroups()
      Description copied from class: GrouperProvisioningTargetNativeSync
      Per-protocol sensible-default attribute list for groups. See GrouperProvisioningTargetNativeSync.getDefaultNativeAttributeConfigsEntities().
      Overrides:
      getDefaultNativeAttributeConfigsGroups in class GrouperProvisioningTargetNativeSync
    • buildNativeGroupFromJson

      public GrouperProvisioningTargetNativeGroup buildNativeGroupFromJson(com.fasterxml.jackson.databind.JsonNode groupNode)
      Build a native group bean from the raw Datadog group JSON:API envelope. targetId is read from the top-level /id; the attributes map is populated for each entry in GrouperProvisioningTargetNativeSync.effectiveNativeAttributeConfigsGroups() (operator-configured or default) by JSON Pointer (defaults nested under /attributes). Returns null when the JSON is missing or has no id.
    • buildNativeUserFromJson

      public GrouperProvisioningTargetNativeUser buildNativeUserFromJson(com.fasterxml.jackson.databind.JsonNode userNode)
      Build a native user bean from the raw Datadog user JSON:API envelope. targetId is read from the top-level /id; the attributes map is populated for each entry in GrouperProvisioningTargetNativeSync.effectiveNativeAttributeConfigsEntities() (operator-configured or default) by JSON Pointer (defaults nested under /attributes). Returns null when the JSON is missing or has no id.
    • nodeWithGroupType

      public static com.fasterxml.jackson.databind.JsonNode nodeWithGroupType(com.fasterxml.jackson.databind.JsonNode groupNode, String groupType)
      Return a shallow copy of the Datadog group envelope with groupType written into its attributes object, so the default /attributes/groupType pointer resolves. Datadog never returns groupType in the JSON -- the commands know it from which endpoint (roles vs teams) produced the object and overlay it here before capture, the same way the Google connector merges two reads into one node before capturing.

      Null-safe: if groupNode is null or not a JSON object, it is returned unchanged (the build path will then simply skip the missing groupType). The original node is never mutated.

      Parameters:
      groupNode - the per-element JSON:API envelope for a role or team
      groupType - "role" or "team", or null/blank to leave the node unchanged
      Returns:
      a copy with attributes.groupType set, or the original node when not applicable
    • captureGroupJson

      public void captureGroupJson(com.fasterxml.jackson.databind.JsonNode groupNode)
      Build + record a Datadog group from its raw JSON. No-op when sync-back is off or id-less.
    • captureUserJson

      public void captureUserJson(com.fasterxml.jackson.databind.JsonNode userNode)
      Build + record a Datadog user from its raw JSON. No-op when sync-back is off or id-less.
    • captureTeamMemberships

      public void captureTeamMemberships(String targetGroupId, List<DatadogMembership> datadogMemberships)
      Translate a list of Datadog memberships for a given target group id into native membership beans and record them. No-op if reporting is off or input is empty. Used for team memberships (DatadogMembership carries the userId). Unchanged by the raw-JSON migration: Datadog has no membership-from-JSON capture; the membership edges are recorded from the already-parsed beans.
    • captureRoleMemberships

      public void captureRoleMemberships(String targetGroupId, List<DatadogUser> roleUsers)
      Translate a list of Datadog users (role members) for a given target group id into native membership beans and record them. Used for role memberships (the Datadog API returns the user list directly). Unchanged by the raw-JSON migration.
    • captureGroupJsonFromCurrentProvisioner

      public static void captureGroupJsonFromCurrentProvisioner(com.fasterxml.jackson.databind.JsonNode groupNode)
      Capture a Datadog group (from its raw JSON:API envelope) against the current provisioner's sync. No-op if there's no current provisioner or the active provisioner isn't a Datadog one. Called from the commands seam (retrieveRoles/retrieveTeams/retrieveGroup) for every role/team read. The envelope should already carry groupType via nodeWithGroupType(JsonNode, String).
    • captureUserJsonFromCurrentProvisioner

      public static void captureUserJsonFromCurrentProvisioner(com.fasterxml.jackson.databind.JsonNode userNode)
      Capture a Datadog user (from its raw JSON:API envelope) against the current provisioner's sync. No-op if there's no current provisioner or the active provisioner isn't a Datadog one. Called from the commands seam (retrieveUsers/retrieveUserByEmail/getRoleUsers).
    • captureTeamMembershipsFromCurrentProvisioner

      public static void captureTeamMembershipsFromCurrentProvisioner(String targetGroupId, List<DatadogMembership> datadogMemberships)
      Capture team memberships against the current provisioner's sync.
    • captureRoleMembershipsFromCurrentProvisioner

      public static void captureRoleMembershipsFromCurrentProvisioner(String targetGroupId, List<DatadogUser> roleUsers)
      Capture role memberships against the current provisioner's sync.
    • captureMembershipInsertFromCurrentProvisioner

      public static void captureMembershipInsertFromCurrentProvisioner(String groupTargetId, String userTargetId)
      Write-track a successful Datadog membership add (addUserToTeam/addUserToRole) against the current provisioner: record (groupTargetId, userTargetId) in the native membership map so the end-of-run flush inserts its grouper_prov_mship row. No-op out of cycle or for a non-Datadog provisioner (and internally no-op when membership sync-back is off).
    • captureMembershipDeleteFromCurrentProvisioner

      public static void captureMembershipDeleteFromCurrentProvisioner(String groupTargetId, String userTargetId)
      Write-track a successful Datadog membership remove (removeUserFromTeam/ removeUserFromRole) against the current provisioner: drop (groupTargetId, userTargetId) from the native membership map so the end-of-run flush deletes its grouper_prov_mship row. No-op out of cycle or for a non-Datadog provisioner (and internally no-op when membership sync-back is off).