#!/usr/bin/env bash set -euo pipefail # exit on error (-e), undefined vars (-u), and propagate pipeline failures (-o pipefail) # graphql-search.sh — Look up one or more Salesforce entities in schema.graphql. # # Run from the SFDX project root, where schema.graphql lives: # bash /scripts/graphql-search.sh Account # bash /scripts/graphql-search.sh Account Contact Opportunity # # The script does NOT search up the directory tree — an ancestor schema.graphql # may belong to a DIFFERENT org, and silently matching it would validate fields # against the wrong schema. If schema.graphql is not at ./ you must point the # script at the right file explicitly (resolution order, first hit wins): # 1. -s / --schema explicit flag # 2. $GRAPHQL_SCHEMA env var explicit override # 3. ./schema.graphql (current working directory only) # # bash /scripts/graphql-search.sh -s force-app/main/default/uiBundles//schema.graphql Account # GRAPHQL_SCHEMA=/abs/path/schema.graphql bash /scripts/graphql-search.sh Account # # Output sections per entity: # 1. Type definition — all fields and relationships # 2. Filter options — _Filter input (for `where:`) # 3. Sort options — _OrderBy input (for `orderBy:`) # 4. Create mutation wrapper — CreateInput # 5. Create mutation fields — CreateRepresentation (for create mutations) # 6. Update mutation wrapper — UpdateInput # 7. Update mutation fields — UpdateRepresentation (for update mutations) SCHEMA="" # ── Argument parsing ───────────────────────────────────────────────────────── while [[ $# -gt 0 ]]; do case "$1" in -s|--schema) if [[ -z "${2-}" || "$2" == -* ]]; then echo "ERROR: --schema requires a file path argument" exit 1 fi SCHEMA="$2" shift 2 ;; --) shift break ;; -*) echo "ERROR: Unknown option: $1" echo "Usage: bash $0 [-s ] [EntityName2 ...]" exit 1 ;; *) break ;; esac done if [ $# -eq 0 ]; then echo "Usage: bash $0 [-s ] [EntityName2 ...]" echo "Example: bash $0 Account" echo "Example: bash $0 Account Contact Opportunity" echo "Example: bash $0 --schema /path/to/schema.graphql Account" exit 1 fi # ── Schema resolution ──────────────────────────────────────────────────────── # NO directory walk-up: resolve only from an explicit flag, an explicit env var, # or schema.graphql in the CURRENT directory. Searching ancestors risks matching # an unrelated project's schema and validating fields against the wrong org. # Resolution order (first hit wins): -s/--schema, then $GRAPHQL_SCHEMA, then ./schema.graphql. if [ -z "$SCHEMA" ] && [ -n "${GRAPHQL_SCHEMA-}" ]; then SCHEMA="$GRAPHQL_SCHEMA" fi if [ -z "$SCHEMA" ]; then SCHEMA="./schema.graphql" fi if [ ! -f "$SCHEMA" ]; then echo "ERROR: schema.graphql not found at '$SCHEMA'." echo " This script does NOT search parent directories (an ancestor schema may be the wrong org's)." echo " Run from the SFDX project root where schema.graphql lives, or point at it explicitly:" echo " bash $0 --schema " echo " GRAPHQL_SCHEMA= bash $0 " echo " If the file is missing entirely, generate it from the UI bundle dir:" echo " cd force-app/main/default/uiBundles/ && npm run graphql:schema" exit 1 fi if [ ! -r "$SCHEMA" ]; then echo "ERROR: schema.graphql is not readable at $SCHEMA" echo " Check file permissions: ls -la $SCHEMA" exit 1 fi if [ ! -s "$SCHEMA" ]; then echo "ERROR: schema.graphql is empty at $SCHEMA" echo " Regenerate it from the UI bundle dir:" echo " cd force-app/main/default/uiBundles/ && npm run graphql:schema" exit 1 fi # Announce the resolved schema so a wrong-file match is visible, not silent. # stderr keeps it out of the parsed lookup output on stdout. echo "[graphql-search] using schema: $SCHEMA" >&2 # ── Helper: extract lines from a grep match through the closing brace ──────── # Prints up to MAX_LINES lines after (and including) the first match of PATTERN. # Uses a generous line count — blocks are always closed by a "}" line. extract_block() { local label="$1" local pattern="$2" local max_lines="$3" local match local grep_exit=0 match=$(grep -nE "$pattern" "$SCHEMA" | head -1) || grep_exit=$? if [ "$grep_exit" -eq 2 ]; then echo " ERROR: grep failed on pattern: $pattern" >&2 return 1 fi if [ -z "$match" ]; then echo " (not found: $pattern)" return 3 fi echo "### $label" grep -E "$pattern" "$SCHEMA" -A "$max_lines" | \ awk '/^\}$/{print; exit} {print}' | \ head -n "$max_lines" || true echo "" } # ── Main loop ──────────────────────────────────────────────────────────────── for ENTITY in "$@"; do # Validate entity name: must be a valid PascalCase identifier (letters, digits, underscores) if [[ ! "$ENTITY" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]]; then echo "ERROR: Invalid entity name: '$ENTITY'" echo " Entity names must start with a letter or underscore, followed by letters, digits, or underscores." echo " Examples: Account, My_Custom_Object__c" continue fi echo "" echo "======================================================================" echo " SCHEMA LOOKUP: $ENTITY" echo "======================================================================" echo "" found=0 # Helper: call extract_block, track matches, surface errors try_extract() { local rc=0 extract_block "$@" || rc=$? if [ "$rc" -eq 0 ]; then found=$((found + 1)) elif [ "$rc" -eq 1 ]; then echo " Aborting lookup for '$ENTITY' due to grep error" >&2 fi # rc=3 is not-found — continue silently (already printed by extract_block) } # 1. Type definition — all fields and relationships try_extract \ "Type definition — fields and relationships" \ "^type ${ENTITY} implements Record" \ 200 # 2. Filter input — used in `where:` arguments try_extract \ "Filter options — use in where: { ... }" \ "^input ${ENTITY}_Filter" \ 100 # 3. OrderBy input — used in `orderBy:` arguments try_extract \ "Sort options — use in orderBy: { ... }" \ "^input ${ENTITY}_OrderBy" \ 60 # 4. Create mutation wrapper try_extract \ "Create mutation wrapper — ${ENTITY}CreateInput" \ "^input ${ENTITY}CreateInput" \ 10 # 5. Create mutation fields try_extract \ "Create mutation fields — ${ENTITY}CreateRepresentation" \ "^input ${ENTITY}CreateRepresentation" \ 100 # 6. Update mutation wrapper try_extract \ "Update mutation wrapper — ${ENTITY}UpdateInput" \ "^input ${ENTITY}UpdateInput" \ 10 # 7. Update mutation fields try_extract \ "Update mutation fields — ${ENTITY}UpdateRepresentation" \ "^input ${ENTITY}UpdateRepresentation" \ 100 if [ "$found" -eq 0 ]; then echo "WARNING: No schema entries found for '$ENTITY'." echo " - Names are PascalCase (e.g., 'Account' not 'account')" echo " - Custom objects may need deployment first" fi echo "" done