diff --git a/.devcontainer/CODESPACES-QUICKSTART.md b/.devcontainer/CODESPACES-QUICKSTART.md index b0df69ba..506723a1 100644 --- a/.devcontainer/CODESPACES-QUICKSTART.md +++ b/.devcontainer/CODESPACES-QUICKSTART.md @@ -3,6 +3,7 @@ ## ✅ What's ready for you Your Codespace has automatically set up: + - Python virtual environment (`.venv`) - Azure CLI - All project dependencies @@ -31,6 +32,7 @@ az login --tenant ## 🚀 Ready to go? ### Start with a sample + 1. Open any notebook in `infrastructure/` or `samples/` 2. If prompted, select the **Python (.venv)** kernel 3. Run the cells @@ -38,6 +40,7 @@ az login --tenant Each folder has a `README.md` with full deployment steps. ### Use the Developer CLI + ```bash bash start.sh ``` @@ -47,7 +50,7 @@ bash start.sh ## 📋 If something isn't working | Issue | Solution | -|-------|----------| +| --- | --- | | Virtual environment not active | New terminals auto-activate. If you see no `(.venv)` prefix, run: `source .venv/bin/activate` | | Extensions not ready | Wait for VS Code status bar to stop spinning (~1 minute) | | Need to verify setup | Run: `python setup/verify_local_setup.py` | @@ -55,5 +58,4 @@ bash start.sh --- - Ready? Start with the README in the root folder for the big picture! diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 216e5fba..19cbb210 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -4,6 +4,7 @@ This directory contains the optimized dev container configuration for the Azure ## 📋 Table of Contents + - [Overview](#overview) - [Files in this Directory](#files-in-this-directory) - [Setup Stages](#setup-stages) @@ -12,6 +13,7 @@ This directory contains the optimized dev container configuration for the Azure - [Jupyter Kernel Configuration](#jupyter-kernel-configuration) - [Troubleshooting](#troubleshooting) - [Performance Notes](#performance-notes) + ## đŸŽ¯ Overview @@ -30,7 +32,7 @@ This approach ensures that time-consuming operations happen during container pre ### Core Configuration Files | File | Purpose | Stage | -|------|---------|-------| +| --- | --- | --- | | `python312/devcontainer.json` | Dev container configuration (Python 3.12) | All | | `python312/Dockerfile` | Container image definition (Python 3.12) | Build | | `python313/devcontainer.json` | Dev container configuration (Python 3.13) | All | @@ -43,18 +45,21 @@ This approach ensures that time-consuming operations happen during container pre ### Configuration Details #### `devcontainer.json` (per Python version folder) + - **Features**: Azure CLI, common utilities, Git, Docker-in-Docker - **Extensions**: Python, Jupyter, Bicep, GitHub Copilot, and more - **Lifecycle Commands**: Optimized three-stage setup - **Port Forwarding**: Common development ports (3000, 5000, 8000, 8080) #### `Dockerfile` (per Python version folder) + - **Base Image**: Microsoft's Python 3.12/3.13/3.14 dev container (depending on folder) - **System Dependencies**: Essential packages and tools - **Azure CLI Setup**: Extensions and configuration for Codespaces - **Virtual Environment**: Auto-activation configuration #### `post-start-setup.sh` (shared behavior) + - **Location**: `.devcontainer/post-start-setup.sh` is invoked by each Python variant's `post-start-setup.sh` wrapper - **Environment Verification**: Quick checks and status reporting - **Fallback Installation**: Safety net for missing components @@ -73,6 +78,7 @@ All three are supported and prebuilt; choose the Python runtime that best matche ### âš ī¸ About the "Default" Option GitHub Codespaces will also display a generic **"Default"** dev container option. **Do not use this option** — it will result in: + - Significantly slower startup times (5-10 minutes vs. ~30 seconds) - Missing tools, extensions, and optimizations - Suboptimal development experience @@ -82,16 +88,20 @@ GitHub Codespaces will also display a generic **"Default"** dev container option ## 🚀 Setup Stages ### Stage 1: Container Build (Dockerfile) + **When it runs**: During initial container build **What it does**: + - Installs the selected Python version (3.12, 3.13, or 3.14) and system dependencies - Configures Azure CLI for Codespaces (device code authentication) - Installs Azure CLI extensions (`containerapp`, `front-door`) - Sets up shell auto-activation for virtual environment ### Stage 2: Content Update (devcontainer.json) + **When it runs**: During prebuild when content changes **What it does**: + - Creates Python virtual environment with uv - Installs all Python packages via `uv sync` (pyproject.toml) - Generates environment configuration (`.env` file) @@ -99,8 +109,10 @@ GitHub Codespaces will also display a generic **"Default"** dev container option - Configures Azure CLI settings ### Stage 3: Runtime Verification (post-start-setup.sh) + **When it runs**: Every time the container starts **What it does**: + - Verifies environment setup (< 10 seconds) - Provides status reporting and user guidance - Performs fallback installation if needed @@ -109,6 +121,7 @@ GitHub Codespaces will also display a generic **"Default"** dev container option ## ⚡ Optimization Strategy ### What Moved to Prebuild + - ✅ Python package installation - ✅ Virtual environment creation - ✅ Azure CLI extension installation @@ -117,12 +130,14 @@ GitHub Codespaces will also display a generic **"Default"** dev container option - ✅ VS Code extension installation ### What Stays in Runtime + - ✅ Environment verification - ✅ Status reporting and user guidance - ✅ Fallback installation (safety net) - ✅ Performance timing and completion messages ### Performance Benefits + - **Faster Startup**: Most heavy operations happen during prebuild - **Better UX**: Users see verification instead of installation progress - **Reliability**: Fallback mechanisms ensure robustness @@ -133,7 +148,7 @@ GitHub Codespaces will also display a generic **"Default"** dev container option To further optimize the startup experience, several VS Code extensions are pre-installed in the container image rather than being installed at container startup: | Extension ID | Description | -|-------------|-------------| +| --- | --- | | ms-python.python | Python language support | | ms-python.debugpy | Python debugging | | ms-toolsai.jupyter | Jupyter notebook support | @@ -172,7 +187,7 @@ This pre-installation happens in the Dockerfile and significantly reduces contai ### Prebuild Benefits | Aspect | Without Prebuild | With Prebuild | -|--------|------------------|---------------| +| --- | --- | --- | | **Startup Time** | 5-10 minutes | 10-30 seconds | | **User Experience** | Watch installation progress | See verification only | | **Resource Usage** | Build every time | Build once, use many times | @@ -184,6 +199,7 @@ This pre-installation happens in the Dockerfile and significantly reduces contai Our devcontainer uses two key lifecycle commands optimized for prebuild: #### `onCreateCommand` (Container Creation) + ```bash # Creates Python virtual environment with uv and registers Jupyter kernel # Note: The Python path varies by selected variant (3.12/3.13/3.14) @@ -194,6 +210,7 @@ python -m ipykernel install --user --name=python-venv --display-name='Python (.v ``` #### `updateContentCommand` (Content Updates) + ```bash # Installs Python packages via uv and configures environment source /workspaces/Apim-Samples/.venv/bin/activate && @@ -207,6 +224,7 @@ az extension add --name front-door --only-show-errors ### When Prebuild is Triggered Prebuild automatically occurs when you push changes to: + - `.devcontainer/devcontainer.json` - `.devcontainer/Dockerfile` - `pyproject.toml` (when referenced in `updateContentCommand`) @@ -246,16 +264,19 @@ You can monitor prebuild status in several ways: To refresh the prebuilt container (recommended periodically): #### Method 1: Trigger via Configuration Change + 1. Make a small change to `.devcontainer/devcontainer.json` (e.g., add a comment) 2. Commit and push the change 3. GitHub will automatically trigger a new prebuild #### Method 2: Manual Trigger (if available) + 1. Go to your repository's Codespaces settings 2. Find the prebuild configuration 3. Click "Trigger prebuild" if the option is available #### Method 3: Update Dependencies + 1. Update `pyproject.toml` with newer package versions or add new dependencies 2. Commit and push the changes 3. Prebuild will automatically run with updated dependencies @@ -272,7 +293,7 @@ To refresh the prebuilt container (recommended periodically): ### Prebuild vs Runtime Separation | Operation | Prebuild Stage | Runtime Stage | -|-----------|----------------|---------------| +| --- | --- | --- | | Python packages | ✅ Install all | ❌ Verify only | | Virtual environment | ✅ Create and configure | ❌ Activate only | | Azure CLI extensions | ✅ Install | ❌ Verify only | @@ -289,12 +310,15 @@ The dev container is configured with a standardized Jupyter kernel for optimal P - **Python Path**: `/workspaces/Apim-Samples/.venv/bin/python` ### Kernel Registration Details + The kernel is automatically registered during the prebuild stage using: + ```bash python -m ipykernel install --user --name=python-venv --display-name="Python (.venv)" ``` ### VS Code Kernel Configuration + The `devcontainer.json` includes specific Jupyter settings to ensure proper kernel selection: ```jsonc @@ -313,8 +337,10 @@ For more details on kernel configuration in VS Code, see: [VS Code Issue #130946 ### Common Issues and Solutions #### Virtual Environment Not Found + **Symptom**: Error about missing virtual environment **Solution**: The virtual environment should be created during prebuild. If missing: + ```bash python3.12 -m venv /workspaces/Apim-Samples/.venv source /workspaces/Apim-Samples/.venv/bin/activate @@ -322,28 +348,35 @@ uv sync ``` #### Azure CLI Extensions Missing + **Symptom**: Commands fail with extension not found **Solution**: Extensions should install during prebuild. If missing: + ```bash az extension add --name containerapp az extension add --name front-door ``` #### Jupyter Kernel Not Available + **Symptom**: Kernel not visible in VS Code **Solution**: Re-register the kernel: + ```bash python -m ipykernel install --user --name=python-venv --display-name="Python (.venv)" ``` #### Environment Variables Not Set + **Symptom**: Import errors or path issues **Solution**: Regenerate the `.env` file: + ```bash python setup/local_setup.py --generate-env ``` ### Debug Commands + Useful commands for troubleshooting: ```bash @@ -370,17 +403,21 @@ cat .env ## 📊 Performance Notes ### Typical Timing + - **First Build**: ~5-10 minutes (includes all prebuild operations) - **Subsequent Startups**: ~10-30 seconds (verification only) - **Content Updates**: ~2-5 minutes (package updates during prebuild) ### Monitoring Setup Progress + The post-start script provides real-time feedback: + - **Terminal Output**: Keep the initial terminal open to see progress - **Status Messages**: Clear indicators for each verification step - **Error Handling**: Detailed messages for any issues encountered ### Best Practices + 1. **Keep Initial Terminal Open**: Shows verification progress and status 2. **Wait for Completion**: Let the verification finish before starting work 3. **Check Status Messages**: Review any warnings or errors reported @@ -390,6 +427,7 @@ The post-start script provides real-time feedback: ### Prebuild Refresh Recommendations **When to refresh prebuilds**: + - Monthly maintenance (keep dependencies current) - After major Python package updates - When Azure CLI or extensions have significant updates @@ -397,6 +435,7 @@ The post-start script provides real-time feedback: - Before important development cycles or team onboarding **Quick refresh method**: + ```bash # Add a comment to trigger prebuild # Edit .devcontainer/devcontainer.json and add/update a comment, then: @@ -420,7 +459,5 @@ When modifying the dev container setup: *This dev container configuration is optimized for Azure API Management samples development with fast startup times and comprehensive tooling support.* - - [github-codespaces]: https://github.com/codespaces [vscode-issue-130946]: https://github.com/microsoft/vscode/issues/130946#issuecomment-1899389049 diff --git a/.github/agents/apim-sample-creator.agent.md b/.github/agents/apim-sample-creator.agent.md index 7465735b..19b394e4 100644 --- a/.github/agents/apim-sample-creator.agent.md +++ b/.github/agents/apim-sample-creator.agent.md @@ -7,6 +7,8 @@ argument-hint: "Describe the sample to add, including its sample name, display n user-invocable: true --- +# APIM Sample Creator + You are the specialist for adding new samples to the APIM Samples repository. ## Required Inputs diff --git a/.github/agents/apim-sample-publisher.agent.md b/.github/agents/apim-sample-publisher.agent.md index ec96b99d..59eb9c95 100644 --- a/.github/agents/apim-sample-publisher.agent.md +++ b/.github/agents/apim-sample-publisher.agent.md @@ -6,6 +6,8 @@ argument-hint: "Describe the sample to publish, including its folder name and wh user-invocable: true --- +# APIM Sample Publisher + You are the specialist for publishing an APIM sample after its implementation is ready for a final quality pass. Your job is to leave the repository buttoned up for review or release: documentation is synchronized, public surfaces and search metadata agree, source artifacts are validated, and any remaining manual checks are explicit. ## Scope @@ -61,9 +63,10 @@ Treat search visibility as a publication criterion, not as optional polish. Revi - Remote, session, sleep, clock, or command boundaries are injectable where deterministic tests require them. - Actively edited sample-local modules use module-qualified imports and selective autoreload, not broad autoreload. 6. Confirm every sample-local helper has focused tests under `tests/python/test__helpers.py` covering meaningful success, failure, malformed-input, and cleanup paths without live Azure access. Target at least 95% coverage for changed helper modules. -7. Validate sample-owned structured files. Parse JSON and notebooks, check XML well-formedness, review APIM policy expressions against the allowed policy-expression surface, and compile or lint Bicep when the toolchain is available. -8. Confirm sample-owned APIM policy XML is under `apim-policies/` and KQL is under `queries/`. For migrations, verify every notebook, helper, Bicep, test, script, and documentation reference, including canonical-directory lookup, temporary root-level policy fallback, explicit paths, auto-detection, and missing-file behavior. -9. Run the combined Python quality checks from the repository root: +7. For multi-model inference samples, confirm that every model uses model-specific APIM backends as well as a model-specific backend pool. APIM tracks circuit-breaker state at the backend resource, so sharing a backend across models can allow a `429` or another configured failure from one model to suppress traffic to an otherwise healthy model. +8. Validate sample-owned structured files. Parse JSON and notebooks, check XML well-formedness, review APIM policy expressions against the allowed policy-expression surface, and compile or lint Bicep when the toolchain is available. +9. Confirm sample-owned APIM policy XML is under `apim-policies/` and KQL is under `queries/`. For migrations, verify every notebook, helper, Bicep, test, script, and documentation reference, including canonical-directory lookup, temporary root-level policy fallback, explicit paths, auto-detection, and missing-file behavior. +10. Run the combined Python quality checks from the repository root: ```powershell .\tests\python\check_python.ps1 @@ -73,16 +76,16 @@ Treat search visibility as a publication criterion, not as optional polish. Revi ./tests/python/check_python.sh ``` -10. Run markdownlint on all changed Markdown files and require zero violations. Fix violations instead of disabling rules unless a narrowly scoped suppression is necessary and documented. -11. Export the self-contained presentation and treat warnings about missing images as failures to investigate: +11. Run markdownlint on all changed Markdown files and require zero violations. Fix violations instead of disabling rules unless a narrowly scoped suppression is necessary and documented. +12. Export the self-contained presentation and treat warnings about missing images as failures to investigate: ```bash uv run python setup/export_presentation.py ``` -12. Run the SEO pass when public website content changed. Parse the inline JSON-LD block and `docs/sitemap.xml`, then verify that structured-data entries and visible cards stay synchronized. -13. Preview the staged website when website or deck content changed. Use `uv run python setup/serve_website.py` and review both the landing page and `/slide-deck.html`. Check desktop and narrow layouts when visual changes are material. -14. Inspect the final diff after validation. Do not include generated artifacts such as `build/`, `_site/`, coverage files, or lint reports unless the repository intentionally tracks them. +1. Run the SEO pass when public website content changed. Parse the inline JSON-LD block and `docs/sitemap.xml`, then verify that structured-data entries and visible cards stay synchronized. +2. Preview the staged website when website or deck content changed. Use `uv run python setup/serve_website.py` and review both the landing page and `/slide-deck.html`. Check desktop and narrow layouts when visual changes are material. +3. Inspect the final diff after validation. Do not include generated artifacts such as `build/`, `_site/`, coverage files, or lint reports unless the repository intentionally tracks them. When live Azure validation is relevant but not requested or not available, report the exact notebook scenario and supported infrastructure combinations that remain to be exercised. Do not substitute unit tests for live scenario evidence. diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 652d77e9..4bf4cd28 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -66,6 +66,9 @@ Uniformity, clarity, and ease of use are paramount across all infrastructures an ## General Coding Guidelines - All code, scripts, and configuration must be cross-platform compatible, supporting Windows, Linux, and macOS. If any special adjustments are to be made, please clearly indicate so in comments. +- Treat CodeQL rules as code-generation requirements. Before writing or changing code, consider applicable security queries and use patterns that are structurally safe and recognizable to static analysis. +- Do not validate, allowlist, or sanitize URLs with substring checks. Parse the URL and compare the required scheme, hostname, port, and path components explicitly. In tests, parse structured content and assert exact URL-bearing fields instead of searching serialized text for a URL substring. +- Resolve CodeQL findings at the source. Do not suppress, dismiss, or weaken a security query unless the repository maintainers have documented why the result is a false positive and approved the narrowest possible suppression. - Prioritize clarity, maintainability, and readability in all generated code. - Focus on achieving a Minimal Viable Product (MVP) first, then iterate. - Follow language-specific conventions and style guides (e.g., PEP 8 for Python). @@ -88,13 +91,13 @@ Uniformity, clarity, and ease of use are paramount across all infrastructures an - `/`: Root directory containing the main files and folders. Bicep configuration is stored in `bicepconfig.json`. - The following folders are all at the root level: - - `assets/`: Draw.io diagrams, SVG exports, and images. Static assets such as these should be placed here. Architecture diagrams should be placed in the /diagrams subfolder. - - `docs/`: Source for the public GitHub Pages landing page. See the *GitHub Pages Site* section below for upkeep rules. - - `infrastructure/`: Contains Jupyter notebooks for setting up various API Management infrastructures. When modifying samples, these notebooks should not need to be modified. - - `samples/`: Various policy and scenario samples that can be applied to the infrastructures. - - `setup/`: General setup scripts and configurations for the repository and dev environment setup. - - `shared/`: Shared resources, such as Bicep modules, Python libraries, and other reusable components. - - `tests/`: Contains unit tests for Python code and Bicep modules. This folder should contain all tests for all code in the repository. + - `assets/`: Draw.io diagrams, SVG exports, and images. Static assets such as these should be placed here. Architecture diagrams should be placed in the /diagrams subfolder. + - `docs/`: Source for the public GitHub Pages landing page. See the *GitHub Pages Site* section below for upkeep rules. + - `infrastructure/`: Contains Jupyter notebooks for setting up various API Management infrastructures. When modifying samples, these notebooks should not need to be modified. + - `samples/`: Various policy and scenario samples that can be applied to the infrastructures. + - `setup/`: General setup scripts and configurations for the repository and dev environment setup. + - `shared/`: Shared resources, such as Bicep modules, Python libraries, and other reusable components. + - `tests/`: Contains unit tests for Python code and Bicep modules. This folder should contain all tests for all code in the repository. ## Infrastructure Development Guidelines @@ -103,6 +106,7 @@ Infrastructures live in `infrastructure/[infra-name]/` and provide the foundatio ### Infrastructure File Structure Each infrastructure in `infrastructure/[infra-name]/` must contain: + - `create.ipynb` - Jupyter notebook that deploys the infrastructure - `create_infrastructure.py` - Python helper script for infrastructure creation logic - `main.bicep` - Bicep template for deploying the infrastructure resources @@ -115,24 +119,28 @@ Each infrastructure in `infrastructure/[infra-name]/` must contain: All infrastructure notebooks must follow this exact cell pattern: #### Cell 1: Configure & Create (Markdown) + - Heading: `### đŸ› ī¸ Configure Infrastructure Parameters & Create the Infrastructure` - One-sentence description naming the specific infrastructure - Bold reminder: `â—ī¸ **Modify entries under _User-defined parameters_**.` - Optional: a short note if the infrastructure has unique deployment phases (e.g. private link approval) #### Cell 2: Configure & Create (Python Code) + - Import only `APIM_SKU`, `INFRASTRUCTURE` from `apimtypes`, `InfrastructureNotebookHelper` from `utils`, and `print_ok` from `console` - `USER CONFIGURATION` section with `rg_location`, `index`, and `apim_sku` (comment each with inline description) - `SYSTEM CONFIGURATION` section: instantiate `InfrastructureNotebookHelper` and call `create_infrastructure()` - Final line: `print_ok('All done!')` #### Cell 3: Clean Up (Markdown) + - Heading: `### đŸ—‘ī¸ Clean up resources` - Standard text: "When you're finished experimenting, it's advisable to remove all associated resources from Azure to avoid unnecessary cost. Use the clean-up notebook for that." ### Infrastructure README.md Use this consistent layout: + - **Title** - Name of the architecture (e.g. "Simple API Management Infrastructure") - **Description** - One to two sentences summarising the architecture and its value - **Architecture diagram** - `` tag referencing the SVG in the infrastructure folder @@ -148,6 +156,7 @@ Use this consistent layout: ### Sample File Structure Each sample in `samples/[sample-name]/` must contain: + - `create.ipynb` - Jupyter notebook that deploys and demonstrates the sample - `main.bicep` - Bicep template for deploying sample resources - `README.md` - Documentation explaining the sample, use cases, and concepts @@ -160,6 +169,7 @@ New sample-owned APIM policy XML and KQL files must not be placed at the sample ### New Sample Sync Checklist Whenever a new sample is added: + - Ask for the sample name if it has not been provided. Do not invent it. - Ask for supported infrastructures if they have not been provided. Do not assume "All infrastructures". - Create the sample under `samples/[sample-name]/` unless the user explicitly requests another location. @@ -175,20 +185,25 @@ Whenever a new sample is added: Follow this pattern for **all** sample `create.ipynb` files. Consistency here is critical - users should recognise the layout immediately from having used any other sample: #### Cell 1: Title & Overview (Markdown) + - Notebook title and brief description - Reference to README.md for detailed information #### Cell 2: What This Sample Does (Markdown) + - Bullet list of key actions/demonstrations - Keep focused on user-facing outcomes #### Cell 3: Initialize Notebook Variables (Markdown) + - Heading with note that only USER CONFIGURATION should be modified #### Cell 4: Initialize Notebook Variables (Python Code) + **This cell should be straightforward configuration only. No Azure SDK calls here.** Structure: + 1. Import statements at the top: - Standard library imports (time, json, tempfile, requests, pathlib, datetime) - `utils`, `apimtypes`, `console`, `azure_resources` (including `az`, `get_infra_rg_name`, `get_account_info`) @@ -211,6 +226,7 @@ Structure: **Important:** Do NOT call `az` commands in this cell. Do NOT create a config dictionary. Do NOT initialize deployment outputs. All Azure operations and variable definitions should happen in subsequent operation cells. #### Cell 5+: Functional Cells (Markdown + Code pairs) + - Each logical operation gets a markdown heading cell followed by one or more code cells - Keep educational configuration, scenario steps, and key APIM concepts visible in the notebook. Extract non-educational Python mechanics, such as reusable orchestration, parsing, retries, polling, data transformation, and repeated request setup, into existing shared helpers or focused sample-local Python helper modules. Compose established helpers first; extend one only when the behavior belongs to its existing responsibility, state, and lifecycle. - Follow `shared/python/README.md` for the complete helper and supporting-class strategy, including ownership, dependency direction, state, lifecycle, selective autoreload, promotion, and testing rules. @@ -223,22 +239,26 @@ Structure: **First operation cell (typically deployment):** âš ī¸ **CRITICAL**: Use `nb_helper.deploy_sample()` for all sample deployments. This method: - - Automatically validates the infrastructure exists (checks resource group) - - Prompts user to select or create infrastructure if needed - - Handles all Azure availability checks internally - - Returns deployment outputs including the APIM service name + +- Automatically validates the infrastructure exists (checks resource group) +- Prompts user to select or create infrastructure if needed +- Handles all Azure availability checks internally +- Returns deployment outputs including the APIM service name **Process:** + 1. Print configuration summary using variables from init cell 2. Build `bicep_parameters` dict with sample-specific parameters (e.g., `location`, `costExportFrequency`) - **DO NOT** manually query for APIM services - **DO NOT** pass `apimServiceName` to `bicep_parameters` if the infrastructure already provides it 3. Call `nb_helper.deploy_sample(bicep_parameters)` to deploy Bicep template 4. Call `nb_helper.get_deployment_context(output)` to validate common outputs, then store its fields as **individual variables** (not in a dictionary) - - Example: `apim_name = deployment_context.apim_name`, `apim_gateway_url = deployment_context.apim_gateway_url` - - Continue to use `output.get(...)` or `output.getJson(...)` for sample-specific outputs that are not part of `SampleDeploymentContext` + +- Example: `apim_name = deployment_context.apim_name`, `apim_gateway_url = deployment_context.apim_gateway_url` +- Continue to use `output.get(...)` or `output.getJson(...)` for sample-specific outputs that are not part of `SampleDeploymentContext` **Invalid approach** (do NOT do this): + ```python # ❌ WRONG - Manual APIM service queries apim_list_result = az.run(f'az apim list --resource-group {rg_name}...') @@ -249,6 +269,7 @@ bicep_parameters = {'apimServiceName': {'value': apim_name}} ``` **Valid approach** (do this): + ```python # ✅ CORRECT - Let deploy_sample() handle infrastructure validation bicep_parameters = { @@ -263,6 +284,7 @@ apim_apis = deployment_context.apis ``` **Subsequent cells:** + - Check prerequisites with `if 'variable_name' not in locals(): raise SystemExit(1)` - Use variables directly in code (e.g., `rg_name`, `subscription_id`, `apim_name`) - Do NOT recreate or duplicate variables from previous cells @@ -271,6 +293,7 @@ apim_apis = deployment_context.apis ### Variable Management **Do NOT use a config dictionary.** Use individual variables that flow naturally through cells: + - Init cell defines user and system configuration variables - Deployment cell creates new variables for deployment outputs (e.g., `apim_name`, `app_insights_name`) - Subsequent cells reference these variables directly @@ -278,6 +301,7 @@ apim_apis = deployment_context.apis - Variables created in one cell are automatically available in all subsequent cells Example: + ```python # Init cell apim_sku = APIM_SKU.BASICV2 @@ -298,6 +322,7 @@ storage_account_id = f'/subscriptions/{subscription_id}/...' ### NotebookHelper Usage **What NotebookHelper does:** + - `__init__()`: Initializes with sample folder, resource group name, location, infrastructure type, and supported infrastructure list - `deploy_sample(bicep_parameters)`: Orchestrates the complete deployment process: 1. Checks if the desired resource group/infrastructure exists @@ -308,7 +333,9 @@ storage_account_id = f'/subscriptions/{subscription_id}/...' - `create_apim_requests(apim_gateway_url, subscription_key=None, headers=None)`: Resolves the selected infrastructure endpoint and creates a configured `ApimRequests` client. **How to use:** + 1. Initialize in the configuration cell (Cell 4): + ```python nb_helper = utils.NotebookHelper( sample_folder, @@ -322,6 +349,7 @@ storage_account_id = f'/subscriptions/{subscription_id}/...' ``` 2. Call in the deployment cell (Cell 5+): + ```python bicep_parameters = { 'location': {'value': rg_location}, @@ -331,15 +359,17 @@ storage_account_id = f'/subscriptions/{subscription_id}/...' ``` 3. Extract common and sample-specific outputs: - ```python - deployment_context = nb_helper.get_deployment_context(output) - apim_name = deployment_context.apim_name - apim_gateway_url = deployment_context.apim_gateway_url - apim_apis = deployment_context.apis + + ```python + deployment_context = nb_helper.get_deployment_context(output) + apim_name = deployment_context.apim_name + apim_gateway_url = deployment_context.apim_gateway_url + apim_apis = deployment_context.apis app_insights_name = output.get('applicationInsightsName') ``` **CRITICAL: Do not bypass NotebookHelper!** + - ❌ Do NOT manually check `az group exists` - ❌ Do NOT manually query `az apim list` to find APIM services - ❌ Do NOT check if resources exist before deployment @@ -395,17 +425,19 @@ Match the heading emojis, heading levels, and section ordering exactly. If a sec #### Prerequisites rules -- **Do NOT repeat general prerequisites** (Azure subscription, Azure CLI, Python environment, APIM instance). These are documented once in the root README's [Getting Started](../../README.md#-getting-started) section and apply to all samples. The APIM Samples Developer CLI (`start.ps1` / `start.sh`) handles environment setup. +- **Do NOT repeat general prerequisites** (Azure subscription, Azure CLI, Python environment, APIM instance). These are documented once in the [root README](../README.md), under Getting Started, and apply to all samples. The APIM Samples Developer CLI (`start.ps1` / `start.sh`) handles environment setup. - **Only add `## ✅ Prerequisites`** when a sample has genuinely unique requirements that go beyond the root README, such as: - Additional Azure RBAC role assignments beyond Contributor - External service accounts (e.g., a Spotify developer account) - Special tooling or configuration not covered by the Developer CLI - When a Prerequisites section is needed, **open with a one-line reference** to the root README for general prerequisites, then list only the sample-specific requirements. Example: + ```markdown ## ✅ Prerequisites Beyond the [general prerequisites](../../README.md#-getting-started) (Azure subscription, CLI, Python environment), this sample requires ... ``` + - The `oauth-3rd-party` sample is the canonical example of sample-specific prerequisites (external service accounts). The `costing` sample is the canonical example of additional RBAC requirements. ### Testing and Traffic Generation @@ -416,6 +448,7 @@ Match the heading emojis, heading levels, and section ordering exactly. If a sec - **One session per cell is fine.** Each notebook cell should be independently runnable, so create and close a session within the same cell rather than sharing one across cells. - For `ApimRequests`, use `nb_helper.create_apim_requests(...)` to resolve the correct endpoint URL, headers, and TLS behavior. Use `utils.get_endpoint(...)` directly only when constructing a raw session for high-volume traffic. - Session pattern example (preferred for loops): + ```python import requests as http_requests @@ -435,7 +468,9 @@ Match the heading emojis, heading levels, and section ordering exactly. If a sec finally: session.close() ``` + - ApimRequests example (for structured test verification with logging): + ```python from apimtesting import ApimTesting @@ -460,10 +495,10 @@ When designing or restructuring a sample notebook: ## Language-specific Instructions - - Python: see `.github/python.instructions.md` - - Bicep: see `.github/bicep.instructions.md` - - JSON: see `.github/json.instructions.md` - - Markdown: see `.github/markdown.instructions.md` +- Python: see `.github/python.instructions.md` +- Bicep: see `.github/bicep.instructions.md` +- JSON: see `.github/json.instructions.md` +- Markdown: see `.github/markdown.instructions.md` ## Formatting and Style @@ -491,17 +526,18 @@ The public landing page at is bu **Treat `docs/index.html` as a downstream consumer of the README.** When you change any of the following, update the landing page in the same PR: -| Change | Update required in `docs/index.html` | Also update | -|---|---|---| -| Add / remove / rename an **infrastructure** | Add / remove / rename the matching `.infra-card` **and** the matching `ListItem` in the JSON-LD `ItemList` (in ``). Update the infrastructure count in the first `.value-card` if it still says "Five". | Add / remove the SVG copy line in `.github/workflows/github-pages.yml`. | -| Add / remove / rename a **sample** | Add / remove / rename the matching `.sample-card` **and** the matching `ListItem` in the JSON-LD `ItemList`. | — | -| Change a sample's **supported infrastructures** | Update the `.infra-tag` text on that sample's card. | — | -| Change the **quick-start flow** in the root README | Update the four `.step` items. | — | -| Rename or replace an **architecture SVG** in `assets/diagrams/` | — | Update the matching `cp` line in `.github/workflows/github-pages.yml`. | +| Change | Update required in `docs/index.html` | Also update | +| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | +| Add / remove / rename an **infrastructure** | Add / remove / rename the matching `.infra-card` **and** the matching `ListItem` in the JSON-LD `ItemList` (in ``). Update the infrastructure count in the first `.value-card` if it still says "Five". | Add / remove the SVG copy line in `.github/workflows/github-pages.yml`. | +| Add / remove / rename a **sample** | Add / remove / rename the matching `.sample-card` **and** the matching `ListItem` in the JSON-LD `ItemList`. | — | +| Change a sample's **supported infrastructures** | Update the `.infra-tag` text on that sample's card. | — | +| Change the **quick-start flow** in the root README | Update the four `.step` items. | — | +| Rename or replace an **architecture SVG** in `assets/diagrams/` | — | Update the matching `cp` line in `.github/workflows/github-pages.yml`. | Check `docs/README.md` for local preview instructions and styling notes. The page is deliberately plain static HTML + an external stylesheet (`docs/styles.css`), with no executable JavaScript and no build tooling, so that it cannot rot due to a transitive npm dependency. The only `