validate_app_folder()

validate_app_folder()#

validate_app_folder(folder, required_files)

Purpose: Sanity-check an app deployment folder before registration. Confirms files exist, JSON parses, and prints a compact summary—without throwing KeyError if some identity fields are missing.

What it does#

  1. File existence Verifies each entry in required_files exists under folder. → Missing list is printed; returns False.

  2. JSON validity & merge Parses every *.json file and merges objects into a single dict (dict.update → later files overwrite earlier keys). → Any parse error or non-object JSON prints a clear message; returns False.

  3. Friendly summary (safe lookups) Prints:

    • App ID / Name / Version — missing fields show as "(missing)" (no exceptions).

    • Parameters / Inputs / Outputs — prints their id values if present; handles list or dict forms.

    • Top-level keys detected in the merged JSON. → Returns True on success.

Signature#

validate_app_folder(folder, required_files) -> bool

Parameters

  • folder (str | os.PathLike): Path to the version directory (e.g., apps/opensees-mp/1.0/).

  • required_files (Iterable[str]): Expected filenames (e.g., ["app.json","profile.json","tapisjob_app.sh"]).

Returns

  • True — all files present and JSON valid.

  • False — a file is missing or a JSON file is invalid.

Notes & tips#

  • No KeyError: Identity fields (id, name, version) are printed using .get(...), so missing fields don’t crash validation.

  • Overwrite rule: If multiple JSON files define the same key, the last one wins. Keep identity fields in a single source of truth (usually app.json).

  • Schema checks: This is syntactic validation. If you want to enforce required keys/structures, add a JSON Schema step later in your pipeline.

Example#

required = ["app.json", "profile.json", "tapisjob_app.sh"]
if not validate_app_folder("./apps/opensees-mp/1.0", required):
    print("Fix the issues above and try again.")

Files#

You can find these files in Community Data.

validate_app_folder.py
def validate_app_folder(folder, required_files):
    """
    Validate an app deployment folder:
      1) Check that all `required_files` exist under `folder`.
      2) Parse any `.json` files and merge their objects (later files overwrite earlier keys).
      3) Print a concise summary (id, name, version, params/inputs/outputs, top-level keys).

    Safe behavior:
      - Never raises KeyError for missing identity keys; prints "(missing)" instead.
      - Handles non-list or malformed `parameters/inputs/outputs` gracefully.

    Parameters
    ----------
    folder : str | os.PathLike
        Path to the app/version directory (e.g., "apps/opensees-mp/1.0/").
    required_files : Iterable[str]
        Filenames expected inside `folder` (e.g., ["app.json", "profile.json", "tapisjob_app.sh"]).

    Returns
    -------
    bool
        True  -> all required files present and all JSON parsed successfully.
        False -> at least one file missing OR a JSON parse error occurred.

    Notes
    -----
    - If multiple JSON files are in `required_files`, later files overwrite earlier keys.
    - Only performs **syntactic** JSON validation (no schema validation).
    """
    import os, json
    from pathlib import Path

    folder_path = Path(folder)
    print(f"🔍 Validating app folder: {folder_path}\n")

    # 1) Presence check
    missing = [f for f in required_files if not (folder_path / f).exists()]
    if missing:
        print(f"❌ Missing required files: {missing}")
        return False
    else:
        print("✅ All required files are present.\n")

    # 2) Parse & merge JSON files
    app_def = {}
    for fname in required_files:
        if Path(fname).suffix.lower() == ".json":
            fpath = folder_path / fname
            try:
                with fpath.open() as fp:
                    obj = json.load(fp)
                if isinstance(obj, dict):
                    app_def.update(obj)  # later files override earlier keys
                else:
                    print(f"❌ {fname} contains JSON that is not an object (dict).")
                    return False
            except json.JSONDecodeError as exc:
                print(f"❌ {fname} is not valid JSON: {exc}")
                return False

    # 3) Friendly summary (safe lookups)
    app_id      = app_def.get("id", "(missing)")
    app_name    = app_def.get("name", "(missing)")
    app_version = app_def.get("version", "(missing)")

    def _collect_ids(val, key="id"):
        # Accepts list[dict], dict (single), or anything else -> robust fallback
        if isinstance(val, list):
            return [str(x.get(key, "(no-id)")) for x in val if isinstance(x, dict)]
        if isinstance(val, dict):
            return [str(val.get(key, "(no-id)"))]
        return []

    param_ids  = _collect_ids(app_def.get("parameters", []), "id")
    input_ids  = _collect_ids(app_def.get("inputs", []), "id")
    output_ids = _collect_ids(app_def.get("outputs", []), "id")

    print(f"📄 App ID: {app_id}")
    print(f"📄 App Name: {app_name}")
    print(f"📄 Version: {app_version}")
    print(f"🔧 Parameters: {param_ids}")
    print(f"📦 Inputs: {input_ids}")
    print(f"📤 Outputs: {output_ids}")
    print(f"\nApp Keys: {list(app_def.keys())}")
    print("\n✅ Basic validation complete. App folder looks good!")
    return True