diff --git a/scripts/enterprise-server.sh b/scripts/enterprise-server.sh new file mode 100755 index 0000000..7c8b0a5 --- /dev/null +++ b/scripts/enterprise-server.sh @@ -0,0 +1,123 @@ +#!/usr/bin/env bash +# Start, stop or inspect the local "enterprise" QQQ test server: qqq-all's enterprise profile +# (every locally runnable qbit + the qqq sample app) on port 8078, MOCK auth (any user, admin). +# +# enterprise-server.sh [--start] | --status | --stop | --build | --log +# +# It only ever signals the pid it started (pid file in the state dir) and the two Docker +# containers it names (qqq-enterprise-redis, qqq-enterprise-opensearch). It never touches the +# 8077 sample server. +# +# Env: +# QQQ_ALL_ENTERPRISE_DIR qqq-all checkout on feat/enterprise-terminal-test +# (default ../.worktrees/qqq-all-enterprise next to this repo's parent) +# QQQ_ENTERPRISE_M2 Maven local repo holding the qqq build to run against +# (default ../.worktrees/qqq-fixes-m2: UI API 1.9.0 fix branch) +# QQQ_SAMPLE_CLASSES compiled qqq-sample-project classes to merge in +# (default ../.worktrees/qqq-fixes/qqq-sample-project/target/classes; +# missing = the sample app is left out) +# QQQ_ENTERPRISE_STATE pid, log and data directory (default ${TMPDIR:-/tmp}/qqq-enterprise-8078) +# QQQ_ENTERPRISE_PORT HTTP port (default 8078) +# QQQ_ENTERPRISE_DOCKER 0 = do not start Redis/OpenSearch containers (quick search and redis are +# then left out of the instance) +# QQQ_ALL_ENTERPRISE_EXCLUDE comma-separated feature names to leave out (see EnterpriseFeatures) +set -euo pipefail + +self="${BASH_SOURCE[0]}"; while [[ -L "$self" ]]; do self="$(readlink "$self")"; done +here="$(cd "$(dirname "$self")" && pwd)" +repo_root="$(cd "$here/.." && pwd)" +# worktrees live in /.worktrees; this repo is either /qqq-cli or /.worktrees/ +qrun_root="$(cd "$repo_root/.." && pwd)"; [[ "$(basename "$qrun_root")" == ".worktrees" ]] && qrun_root="$(cd "$qrun_root/.." && pwd)" +qqq_all="${QQQ_ALL_ENTERPRISE_DIR:-$qrun_root/.worktrees/qqq-all-enterprise}" +m2="${QQQ_ENTERPRISE_M2:-$qrun_root/.worktrees/qqq-fixes-m2}" +sample_classes="${QQQ_SAMPLE_CLASSES:-$qrun_root/.worktrees/qqq-fixes/qqq-sample-project/target/classes}" +port="${QQQ_ENTERPRISE_PORT:-8078}" +tmp_root="${TMPDIR:-/tmp}"; state_dir="${QQQ_ENTERPRISE_STATE:-${tmp_root%/}/qqq-enterprise-$port}" +state_dir="${state_dir%/}" +pid_file="$state_dir/server.pid" +log_file="$state_dir/server.log" +data_dir="$state_dir/data" +jar="$qqq_all/qqq-all-app/target/qqq-all-app.jar" +redis_name="qqq-enterprise-redis"; redis_port=6390 +search_name="qqq-enterprise-opensearch"; search_port=9210 +mkdir -p "$state_dir" + +running() { [[ -f "$pid_file" ]] && kill -0 "$(cat "$pid_file")" 2>/dev/null; } +use_docker() { [[ "${QQQ_ENTERPRISE_DOCKER:-1}" != "0" ]] && command -v docker > /dev/null && docker info > /dev/null 2>&1; } + +container_up() # name image port container-port [docker args...] +{ + local name="$1" image="$2" host_port="$3" container_port="$4"; shift 4 + if [[ "$(docker inspect -f '{{.State.Running}}' "$name" 2>/dev/null)" == "true" ]]; then return 0; fi + if docker inspect "$name" > /dev/null 2>&1; then docker start "$name" > /dev/null; return 0; fi + docker run -d --name "$name" -p "127.0.0.1:$host_port:$container_port" "$@" "$image" > /dev/null +} + +build() +{ + [[ -f "$qqq_all/pom.xml" ]] || { echo "no qqq-all checkout at $qqq_all (set QQQ_ALL_ENTERPRISE_DIR)"; exit 1; } + echo "building qqq-all-app (enterprise profile) against $m2 …" + mvn -B -q -f "$qqq_all/pom.xml" -Dmaven.repo.local="$m2" -DskipTests -pl qqq-all-app -am package + echo "built $jar" +} + +case "${1:---start}" in + --build|build) build ;; + --start|start) + if running; then echo "already running (pid $(cat "$pid_file")) on http://localhost:$port"; exit 0; fi + if curl -sf -m 2 "http://localhost:$port/qqq/v1/version" > /dev/null 2>&1; then + echo "port $port is already serving something that this script did not start; not starting"; exit 1 + fi + [[ -f "$jar" ]] || build + env_args=(QQQ_ALL_PROFILE=enterprise QQQ_ALL_PORT="$port" QQQ_ALL_DATA_DIR="$data_dir") + if use_docker; then + echo "starting Redis ($redis_name, :$redis_port) and OpenSearch ($search_name, :$search_port) containers" + container_up "$redis_name" redis:7.4-alpine "$redis_port" 6379 + container_up "$search_name" opensearchproject/opensearch:2.19.1 "$search_port" 9200 \ + -e discovery.type=single-node -e DISABLE_SECURITY_PLUGIN=true -e "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" + for _ in $(seq 1 60); do curl -sf -m 2 "http://127.0.0.1:$search_port/_cluster/health" > /dev/null 2>&1 && break; sleep 2; done + env_args+=(QQQ_ALL_OPENSEARCH_HOST=127.0.0.1 QQQ_ALL_OPENSEARCH_PORT="$search_port" + QQQ_ALL_REDIS_HOST=127.0.0.1 QQQ_ALL_REDIS_PORT="$redis_port") + else + echo "Docker not used: quick search and redis are left out" + fi + classpath="$jar" + if [[ -d "$sample_classes" ]]; then classpath="$jar:$sample_classes"; else echo "no sample classes at $sample_classes: sample app left out"; fi + echo "starting enterprise server on port $port (log: $log_file, data: $data_dir)" + nohup env "${env_args[@]}" java -cp "$classpath" com.kingsrook.qqq.all.ProfileLauncher > "$log_file" 2>&1 & + echo $! > "$pid_file" + for _ in $(seq 1 90); do + if curl -sf -m 2 "http://localhost:$port/qqq/v1/version" > /dev/null 2>&1; then + echo "ready: http://localhost:$port (UI API $(curl -s "http://localhost:$port/qqq/v1/version" | sed 's/.*"apiVersion":"\([^"]*\)".*/\1/'))" + echo "login: qqq login --server http://localhost:$port (mock auth: any user is an all-access admin)" + exit 0 + fi + if ! running; then echo "server exited early; see $log_file"; tail -20 "$log_file"; rm -f "$pid_file"; exit 1; fi + sleep 2 + done + echo "timed out waiting for the server; see $log_file"; exit 1 + ;; + --stop|stop) + if running; then + kill "$(cat "$pid_file")" 2>/dev/null || true + for _ in $(seq 1 15); do running || break; sleep 1; done + running && kill -9 "$(cat "$pid_file")" 2>/dev/null || true + echo "stopped pid $(cat "$pid_file")" + else + echo "not running (no live pid in $pid_file)" + fi + rm -f "$pid_file" + if use_docker; then docker stop "$redis_name" "$search_name" > /dev/null 2>&1 && echo "stopped containers $redis_name $search_name" || true; fi + ;; + --status|status) + if running; then + echo "running: pid $(cat "$pid_file") on http://localhost:$port" + curl -s -m 3 "http://localhost:$port/qqq/v1/version" | python3 -c 'import json,sys; d=json.load(sys.stdin); print("UI API", d["apiVersion"]); print("extensions:", ", ".join(e["name"]+" "+e.get("version","?") for e in d.get("extensions",[])))' 2>/dev/null || echo "(not answering on /qqq/v1/version)" + else + echo "not running" + fi + echo "log: $log_file" + ;; + --log|log) tail -n 100 -f "$log_file" ;; + *) echo "usage: $0 [--start] | --status | --stop | --build | --log"; exit 2 ;; +esac diff --git a/scripts/enterprise-smoke.sh b/scripts/enterprise-smoke.sh new file mode 100755 index 0000000..265d13e --- /dev/null +++ b/scripts/enterprise-smoke.sh @@ -0,0 +1,109 @@ +#!/usr/bin/env bash +# CLI sweep of every qbit surface on the enterprise test server (scripts/enterprise-server.sh). +# Runs each command, records its exit code and the first lines of output; non-zero lines are the gaps. +# scripts/enterprise-smoke.sh QQQ="qqq --profile enterprise" OUT=file N=8 +# Writes a few records (an audit probe customer, a comment, a follow, a saved view) on the test server. +here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +QQQ_CMD=(${QQQ:-node $here/../packages/cli/dist/main.js}) +OUT=${OUT:-${TMPDIR:-/tmp}/qqq-enterprise-smoke.txt}; : > "$OUT" +DL=${DL:-$(mktemp -d)} +run() { local label="$1"; shift; local o; o=$("${QQQ_CMD[@]}" "$@" 2>&1); local rc=$?; { echo "### [$rc] $label :: qqq $*"; echo "$o" | head -${N:-8} | cut -c1-240; } >> "$OUT"; } +run whoami whoami +run "apps list" apps list -O text +run home home -O json +run "process list count" process list -O json -Q 'length(@)' +run "widget list count" widget list -O json -Q 'length(@)' +run "find customer" find customer +run "search (global)" search Chicago +run "doctor" doctor +# ESB +run "esb overview" esb overview +run "esb table order" esb table order +run "esb process syncOrder" esb process syncOrder +run "esb messages" esb messages orderEvents --trigger syncOrder.orderEvents +# health (not installed on 8078) +run "health summary" health get-summary +# alerts +run "alerts inbox" alerts get-inbox +run "alerts summary" alerts get-inbox-summary +run "alerts record panel" alerts get-record-panel -q tableName=customer recordId=1 +# assistant +run "assistant config" assistant get-config +run "assistant conversations" assistant get-conversations +run "assistant access" assistant get-access +# collaboration +run "collab presence table" collab presence customer +run "collab presence record" collab presence customer 1 +run "collab favorites" collab favorites list +run "collab recents" collab recents +run "collab people" collab people admin +run "collab my-work" collab my-work +run "collab comments list" collab comments list customer 1 +run "collab comments add" collab comments add customer 1 "enterprise smoke comment" +run "collab tags record" collab tags list customer 1 +run "collab follow" collab follow customer 1 +run "collab timeline" collab get-records-timeline customer 1 +run "collab attachments" collab get-records-attachments customer 1 +run "collab share preview" collab post-records-shares-preview customer 1 -b '{"toUserIds":["core:admin"],"message":"look"}' +# data analysis +run "da capabilities customer" data-analysis get-tables-capabilities customer +run "da table profile customer" data-analysis post-tables-table-profile customer -b '{}' +run "da column profile customer.name" data-analysis post-tables-column-profile customer -b '{"fieldName":"name"}' +run "da distinct" data-analysis post-tables-distinct-values order -b '{"fieldName":"status"}' +run "da saved" data-analysis get-saved +# db performance +run "dbperf overview" db-performance get-overview +run "dbperf activity" db-performance get-activity +# qa capture +run "qa capability" qa-capture get-capability +# quick search +run "qs status" quick-search get-status +run "qs suggest" quick-search get-suggest -q q=a +run "table query --search" table query customer --search a +# release notes +run "rn releases" release-notes get-releases +run "rn summary" release-notes get-summary +# secured data +run "sd status" secured-data get-status +run "sd reveal options" secured-data get-reveal-options -q tableName=customer fieldName=email +# sql workbench +run "sqlwb capabilities" sql-workbench get-capabilities +run "sqlwb schema h2" sql-workbench get-schema h2 +run "sqlwb execute" sql-workbench post-execute -b '{"backendName":"h2","sql":"select id, name from customer"}' +run "sqlwb history" sql-workbench get-history +run "sqlwb notebooks" sql-workbench get-notebooks +# jobs +run "job list" job list +run "job list --all" job list --all +# reports / saved reports / views / bulk profiles +run "report list" report list +run "saved-report list" saved-report list +run "saved-view list" saved-view list customer +run "bulk-profile list" bulk-profile list customer +# prefs, help +run "prefs list" prefs list +run "help-search" help-search order +# webhooks (tables) +run "webhook query" table query webhook +run "webhook subscription query" table query webhookSubscription +run "webhook actions" table actions webhook +# scripts +run "script tables" table query script --limit 3 +# exports +run "export customer csv" table export customer --format csv --out $DL/customer.csv +run "export order xlsx" table export order --format xlsx --out $DL/order.xlsx +run "export person json" table export person --format json --out $DL/person.json +run "qs reindex process" process run quickSearchFullReindex --yes +run "qs suggest after reindex" quick-search get-suggest -q q=ada +run "search after reindex" search ada +run "audit create" table create customer --set name="Audit Probe" --set email=probe@example.test +run "audit update" table update customer 1 --set email=ada2@example.test +run "audit list" audit list customer 1 +run "saved view save" saved-view save customer -n "Enterprise smoke" -w "name ~ a" --replace +run "saved view list" saved-view list customer +run "process run calculateOrderTotal" process run calculateOrderTotal -v quantity=3 unitPriceCents=1250 +run "process run greet" process run greet -r 1,2 --yes +run "process describe syncOrder" process describe syncOrder +run "webhook test event" process run SendTestEventToWebhook -r 1 --yes +run "widget get first" widget list -O json -Q '[0:5].name' +echo "done: $(grep -c "^### \[0\]" "$OUT") passed, $(grep -c "^### \[[1-9]" "$OUT") non-zero; $OUT"