{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "sailpoint.isc.transforms.usernameGenerator.schema.json",
  "title": "SailPoint ISC Transform Schema - usernameGenerator",
  "description": "Strict schema for the SailPoint ISC usernameGenerator transform. Generates a unique username by evaluating an ordered list of format string patterns until a unique value is found. Patterns use dollar-sign token notation ($varName or ${varName}) referencing dynamic variables defined as additional attribute keys. IMPORTANT: (1) The pattern containing '${uniqueCounter}' MUST be the last entry — patterns after it are never evaluated. (2) If all patterns are exhausted without finding a unique value, an IllegalStateException is thrown. (3) This transform is designed for account create profiles, not standalone identity profile mappings. The 'name' field is optional when used inside a create profile.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "type",
    "attributes"
  ],
  "properties": {
    "type": {
      "const": "usernameGenerator",
      "description": "Transform operation type. Must be exactly 'usernameGenerator'."
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "description": "Optional display name. Typically omitted when the transform is embedded within an account create profile attribute definition."
    },
    "requiresPeriodicRefresh": {
      "type": "boolean",
      "default": false,
      "description": "If true, re-evaluates this transform during the nightly identity refresh cycle. Default is false."
    },
    "attributes": {
      "type": "object",
      "required": [
        "patterns"
      ],
      "description": "Required container. Must include 'patterns'. Optional cloud control fields: cloudMaxSize, cloudMaxUniqueChecks, cloudRequired, sourceCheck. Any additional keys are dynamic variable definitions — the key name becomes a $token usable in patterns, and its value is a static string or a nested transform that supplies the substitution value.",
      "properties": {
        "patterns": {
          "type": "array",
          "minItems": 1,
          "items": {
            "type": "string",
            "minLength": 1
          },
          "description": "Required. Ordered list of format strings evaluated sequentially until a unique value is found. Each string may contain $varName or ${varName} tokens referencing dynamic variables defined as sibling attribute keys. Rules: (1) The pattern containing '${uniqueCounter}' MUST be the last entry — it auto-increments on each collision and patterns after it are never reached. (2) If no pattern contains '${uniqueCounter}' and all patterns produce values that already exist, the generator throws IllegalStateException. (3) The generator validates uniqueness only for the attribute marked as 'accountID' in the account schema.",
          "examples": [
            ["$fn$ln", "$fn.$ln${uniqueCounter}"],
            ["$fn$ln", "$fi$ln", "$fn$mi$ln${uniqueCounter}"]
          ]
        },
        "sourceCheck": {
          "type": "boolean",
          "default": false,
          "description": "Optional. Determines where uniqueness is validated. true = query the target system directly (only if the source supports the getObject operation — falls back to ISC database for sources that don't). false = check only the ISC database (default). The generator always validates the attribute marked as 'accountID' in the account schema."
        },
        "cloudMaxSize": {
          "type": "integer",
          "minimum": 1,
          "description": "Optional. Maximum character length for generated usernames. Generated values exceeding this length are automatically truncated to this size before the uniqueness check."
        },
        "cloudMaxUniqueChecks": {
          "type": "integer",
          "minimum": 1,
          "maximum": 50,
          "description": "Optional. Maximum number of uniqueness check iterations before the generator throws IllegalStateException. Must be between 1 and 50 (the documented maximum). Applies to the ${uniqueCounter} expansion loop — when the counter exceeds this limit without finding a unique value, the transform fails."
        },
        "cloudRequired": {
          "type": "boolean",
          "description": "Internal flag. Must remain true. Changing this value may cause unexpected behavior."
        }
      },
      "additionalProperties": {
        "description": "Dynamic variable definition. The key name becomes a $token referenced in patterns (e.g., key 'fn' is used as '$fn' or '${fn}' in a pattern). The value can be a static string or a nested transform object whose output supplies the substitution value.",
        "anyOf": [
          {
            "type": "string",
            "description": "Static string value for this variable token."
          },
          {
            "$ref": "#/$defs/NestedTransform"
          }
        ]
      }
    }
  },
  "$defs": {
    "NestedTransform": {
      "type": "object",
      "description": "A nested transform object used as a dynamic variable value. Its output string is substituted for the $token in patterns. Common types: identityAttribute (to read a name attribute), substring (to extract initials), accountAttribute. The 'name' field is optional in nested transforms per SailPoint docs.",
      "additionalProperties": false,
      "required": [
        "type",
        "attributes"
      ],
      "properties": {
        "id": {
          "type": "string",
          "description": "Optional ID when referencing an existing saved transform."
        },
        "name": {
          "type": "string",
          "minLength": 1,
          "description": "Optional display name for the nested transform."
        },
        "type": {
          "type": "string",
          "minLength": 1,
          "description": "The operation type of the nested transform (e.g., 'identityAttribute', 'substring', 'accountAttribute', 'lower')."
        },
        "requiresPeriodicRefresh": {
          "type": "boolean",
          "description": "Whether this nested transform re-evaluates during nightly refresh."
        },
        "attributes": {
          "type": "object",
          "additionalProperties": true,
          "description": "Operation-specific attributes for the nested transform."
        }
      }
    }
  },
  "examples": [
    {
      "name": "Basic Username Generator",
      "type": "usernameGenerator",
      "attributes": {
        "patterns": [
          "$fn$ln",
          "$fn.$ln${uniqueCounter}"
        ],
        "fn": {
          "type": "identityAttribute",
          "attributes": {
            "name": "firstname"
          }
        },
        "ln": {
          "type": "identityAttribute",
          "attributes": {
            "name": "lastname"
          }
        }
      }
    },
    {
      "name": "Username with Initial and Cloud Controls",
      "type": "usernameGenerator",
      "attributes": {
        "sourceCheck": true,
        "cloudMaxSize": 20,
        "cloudMaxUniqueChecks": 50,
        "patterns": [
          "$fn$ln",
          "$fi$ln",
          "$fn$mi$ln${uniqueCounter}"
        ],
        "fn": {
          "type": "identityAttribute",
          "attributes": {
            "name": "firstname"
          }
        },
        "ln": {
          "type": "identityAttribute",
          "attributes": {
            "name": "lastname"
          }
        },
        "fi": {
          "type": "substring",
          "attributes": {
            "begin": 0,
            "end": 1,
            "input": {
              "type": "identityAttribute",
              "attributes": {
                "name": "firstname"
              }
            }
          }
        },
        "mi": {
          "type": "substring",
          "attributes": {
            "begin": 0,
            "end": 1,
            "input": {
              "type": "identityAttribute",
              "attributes": {
                "name": "middleName"
              }
            }
          }
        }
      }
    },
    {
      "name": "Lowercase Email-Style Username",
      "type": "usernameGenerator",
      "attributes": {
        "cloudMaxSize": 64,
        "patterns": [
          "$fn.$ln",
          "$fn.$ln${uniqueCounter}"
        ],
        "fn": {
          "type": "lower",
          "attributes": {
            "input": {
              "type": "identityAttribute",
              "attributes": {
                "name": "firstname"
              }
            }
          }
        },
        "ln": {
          "type": "lower",
          "attributes": {
            "input": {
              "type": "identityAttribute",
              "attributes": {
                "name": "lastname"
              }
            }
          }
        }
      }
    }
  ]
}
