diff --git a/docs/AVMPostDeploymentGuide.md b/docs/AVMPostDeploymentGuide.md index e0a1fe0b..be2593b6 100644 --- a/docs/AVMPostDeploymentGuide.md +++ b/docs/AVMPostDeploymentGuide.md @@ -11,9 +11,10 @@ This document provides guidance on post-deployment steps after deploying the Con After successfully deploying the Content Processing Solution Accelerator using the AVM template, you need to: 1. **Register schemas** — upload schema files, create a schema set, and link them together -2. **Configure authentication** — set up app registration for secure access +2. **Process sample files** — upload and process sample claim bundles for verification +3. **Configure authentication** — set up app registration for secure access -> **Note:** When deploying via `azd up`, schema registration happens automatically through a post-provisioning hook. AVM deployments require the manual steps below. +> **Note:** When deploying via `azd up`, schema registration and sample processing happen automatically through a post-provisioning hook. AVM deployments require the manual steps below. ## Prerequisites @@ -73,14 +74,27 @@ The script is idempotent — it skips schemas and schema sets that already exist > **Want custom schemas?** See [Customize Schema Data](./CustomizeSchemaData.md) to create your own document schemas. -### Step 4: Configure Authentication (Required) +### Step 4: Process Sample File Bundles (Optional) + +After schema registration, you can upload and process the included sample claim bundles to verify the deployment is working end to end. Each sample folder (`claim_date_of_loss/`, `claim_hail/`) contains a `bundle_info.json` manifest that maps files to their schema classes. + +The workflow for each bundle: +1. **Create a claim batch** with the schema set ID via `PUT /claimprocessor/claims` +2. **Upload each file** with its mapped schema ID via `POST /claimprocessor/claims/{claim_id}/files` +3. **Submit the batch** for processing via `POST /claimprocessor/claims` + +You can perform these steps via the web UI or the API directly. See the [API documentation](./API.md) and [Golden Path Workflows](./GoldenPathWorkflows.md) for details. + +> **Note:** When deploying via `azd up`, sample file processing happens automatically as Step 4 of the post-provisioning hook. + +### Step 5: Configure Authentication (Required) **This step is mandatory for application access:** 1. Follow [App Authentication Configuration](./ConfigureAppAuthentication.md). 2. Wait up to 10 minutes for authentication changes to take effect. -### Step 5: Verify Deployment +### Step 6: Verify Deployment 1. Access your application using the Web App URL from your deployment output. 2. Confirm the application loads successfully. diff --git a/docs/DeploymentGuide.md b/docs/DeploymentGuide.md index 6c603a3d..2e0a00ac 100644 --- a/docs/DeploymentGuide.md +++ b/docs/DeploymentGuide.md @@ -308,6 +308,7 @@ Schema registration happens **automatically** as part of the `azd up` post-provi 2. Registers the sample schema files (auto claim, damaged car image, police report, repair estimate) 3. Creates an **"Auto Claim"** schema set 4. Adds all registered schemas into the schema set +5. Processes sample file bundles (`claim_date_of_loss/` and `claim_hail/`) — creates claim batches, uploads files with their mapped schemas, and submits them for processing After successful deployment, the terminal displays container app details and schema registration output: @@ -355,7 +356,29 @@ Schema registration process completed. Schema set ID: Schemas added: 4 ============================================================ - ✅ Schema registration complete. + +============================================================ +Step 4: Process sample file bundles +============================================================ + + 📂 Processing bundle: claim_date_of_loss + ✅ Claim batch created with ID: + ✅ Uploaded 'claim_form.pdf' successfully. + ✅ Uploaded 'damage_photo.png' successfully. + ✅ Uploaded 'police_report.pdf' successfully. + ✅ Uploaded 'repair_estimate.pdf' successfully. + ✅ Claim batch '' submitted for processing. + + 📂 Processing bundle: claim_hail + ✅ Claim batch created with ID: + ✅ Uploaded 'claim_form.pdf' successfully. + ✅ Uploaded 'damage_photo.png' successfully. + ✅ Uploaded 'repair_estimate.pdf' successfully. + ✅ Claim batch '' submitted for processing. + +============================================================ +Sample file processing completed. +============================================================ ``` ### 5.2 Configure Authentication (Required) @@ -373,10 +396,12 @@ Schema registration process completed. ### 5.4 Test the Application +> **Note:** The post-deployment hook automatically uploads and processes two sample claim bundles (`claim_date_of_loss` and `claim_hail`). You can verify the results in the web app immediately after deployment. + **Quick Test Steps:** -1. **Download Samples**: Get sample files from the [samples directory](../src/ContentProcessorAPI/samples) — use the `claim_date_of_loss/` or `claim_hail/` folders for auto claim documents. -2. **Upload**: In the app, select the **"Auto Claim"** schema set, choose a schema (e.g., Auto Insurance Claim Form), click Import Content, and upload a sample file. -3. **Review**: Wait for completion (~1 min), then click the row to verify the extracted data against the source document. +1. **Check Processed Results**: Open the web app — you should see the two sample claim batches already processed with extracted data. +2. **Review**: Click a processed claim row to verify the extracted data against the source document. +3. **Upload More (Optional)**: To test additional uploads, get sample files from the [samples directory](../src/ContentProcessorAPI/samples), select the **"Auto Claim"** schema set, and upload via Import Content. 📖 **Detailed Instructions:** See the complete [Golden Path Workflows](./GoldenPathWorkflows.md) guide for step-by-step testing procedures. diff --git a/infra/scripts/post_deployment.ps1 b/infra/scripts/post_deployment.ps1 index 04104a50..4a0c68cb 100644 --- a/infra/scripts/post_deployment.ps1 +++ b/infra/scripts/post_deployment.ps1 @@ -1,7 +1,7 @@ # Stop script on any error $ErrorActionPreference = "Stop" -Write-Host "[Search] Fetching container app info from azd environment..." +Write-Host "- Fetching container app info from azd environment..." # Load values from azd env $CONTAINER_WEB_APP_NAME = azd env get-value CONTAINER_WEB_APP_NAME @@ -32,25 +32,25 @@ $FullPath = Resolve-Path $DataScriptPath # Output Write-Host "" -Write-Host "[Info] Web App Details:" -Write-Host " [OK] Name: $CONTAINER_WEB_APP_NAME" -Write-Host " [URL] Endpoint: $CONTAINER_WEB_APP_FQDN" -Write-Host " [Link] Portal URL: $WEB_APP_PORTAL_URL" +Write-Host "- Web App Details:" +Write-Host " - Name: $CONTAINER_WEB_APP_NAME" +Write-Host " - Endpoint: $CONTAINER_WEB_APP_FQDN" +Write-Host " - Portal URL: $WEB_APP_PORTAL_URL" Write-Host "" -Write-Host "[Info] API App Details:" -Write-Host " [OK] Name: $CONTAINER_API_APP_NAME" -Write-Host " [URL] Endpoint: $CONTAINER_API_APP_FQDN" -Write-Host " [Link] Portal URL: $API_APP_PORTAL_URL" +Write-Host "- API App Details:" +Write-Host " - Name: $CONTAINER_API_APP_NAME" +Write-Host " - Endpoint: $CONTAINER_API_APP_FQDN" +Write-Host " - Portal URL: $API_APP_PORTAL_URL" Write-Host "" -Write-Host "[Info] Workflow App Details:" -Write-Host " [OK] Name: $CONTAINER_WORKFLOW_APP_NAME" -Write-Host " [Link] Portal URL: $WORKFLOW_APP_PORTAL_URL" +Write-Host "- Workflow App Details:" +Write-Host " - Name: $CONTAINER_WORKFLOW_APP_NAME" +Write-Host " - Portal URL: $WORKFLOW_APP_PORTAL_URL" Write-Host "" -Write-Host "[Package] Registering schemas and creating schema set..." -Write-Host " [Wait] Waiting for API to be ready..." +Write-Host "- Registering schemas and creating schema set..." +Write-Host " - Waiting for API to be ready..." $MaxRetries = 10 $RetryInterval = 15 @@ -61,7 +61,7 @@ for ($i = 1; $i -le $MaxRetries; $i++) { try { $response = Invoke-WebRequest -Uri "$ApiBaseUrl/schemavault/" -Method GET -UseBasicParsing -TimeoutSec 10 -ErrorAction Stop if ($response.StatusCode -eq 200) { - Write-Host " [OK] API is ready." + Write-Host " - API is ready." $ApiReady = $true break } @@ -229,4 +229,132 @@ if (-not $ApiReady) { Write-Host "Schema registration process completed." Write-Host " Schemas registered: $($Registered.Count)" Write-Host ("=" * 60) + + # --- Step 4: Process sample file bundles --- + if ($SchemaSetId -and $Registered.Count -gt 0) { + Write-Host "" + Write-Host ("=" * 60) + Write-Host "Step 4: Process sample file bundles" + Write-Host ("=" * 60) + + $SamplesDir = Resolve-Path (Join-Path $ScriptDir "..\..\src\ContentProcessorAPI\samples") + $BundleFolders = @("claim_date_of_loss", "claim_hail") + $ClaimProcessorUrl = "$ApiBaseUrl/claimprocessor/claims" + + foreach ($bundle in $BundleFolders) { + $bundleDir = Join-Path $SamplesDir $bundle + $bundleInfoPath = Join-Path $bundleDir "bundle_info.json" + + if (-not (Test-Path $bundleInfoPath)) { + Write-Host " Skipping '$bundle' - no bundle_info.json found." + continue + } + + Write-Host "" + Write-Host " Processing bundle: $bundle" + + $bundleManifest = Get-Content $bundleInfoPath -Raw | ConvertFrom-Json + + # Step 4a: Create claim batch with schemaset ID + Write-Host " - Creating claim batch..." + try { + $claimResp = Invoke-RestMethod -Uri $ClaimProcessorUrl -Method PUT ` + -ContentType "application/json" ` + -Body (@{ schema_collection_id = $SchemaSetId } | ConvertTo-Json) ` + -TimeoutSec 30 -ErrorAction Stop + $claimId = $claimResp.claim_id + Write-Host " - Claim batch created with ID: $claimId" + } catch { + Write-Host " - Failed to create claim batch. Error: $_" + continue + } + + # Step 4b: Upload each file with its mapped schema ID + Add-Type -AssemblyName System.Net.Http + $httpClient = New-Object System.Net.Http.HttpClient + $httpClient.Timeout = [TimeSpan]::FromSeconds(60) + $uploadSuccess = $true + foreach ($entry in $bundleManifest.files) { + $schemaClass = $entry.schema_class + $fileName = $entry.file_name + $filePath = Join-Path $bundleDir $fileName + + if (-not (Test-Path $filePath)) { + Write-Host " - File '$fileName' not found. Skipping." + continue + } + + $schemaId = $Registered[$schemaClass] + if (-not $schemaId) { + Write-Host " - No schema ID found for '$schemaClass'. Skipping '$fileName'." + continue + } + + Write-Host " - Uploading '$fileName' (schema: $schemaClass)..." + + $dataPayload = @{ + Claim_Id = $claimId + Schema_Id = $schemaId + Metadata_Id = "sample-$bundle" + } | ConvertTo-Json -Compress + + $fileBytes = [System.IO.File]::ReadAllBytes((Resolve-Path $filePath)) + $mimeType = switch ([System.IO.Path]::GetExtension($fileName).ToLower()) { + ".pdf" { "application/pdf" } + ".png" { "image/png" } + ".jpg" { "image/jpeg" } + ".jpeg" { "image/jpeg" } + default { "application/octet-stream" } + } + + try { + $multipartContent = New-Object System.Net.Http.MultipartFormDataContent + $jsonContent = [System.Net.Http.StringContent]::new($dataPayload, [System.Text.Encoding]::UTF8, "application/json") + $jsonContent.Headers.ContentDisposition = [System.Net.Http.Headers.ContentDispositionHeaderValue]::Parse("form-data; name=`"data`"") + $multipartContent.Add($jsonContent, "data") + + $fileContent = [System.Net.Http.ByteArrayContent]::new($fileBytes) + $fileContent.Headers.ContentDisposition = [System.Net.Http.Headers.ContentDispositionHeaderValue]::Parse("form-data; name=`"file`"; filename=`"$fileName`"") + $fileContent.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::Parse($mimeType) + $multipartContent.Add($fileContent, "file", $fileName) + + $response = $httpClient.PostAsync("$ClaimProcessorUrl/$claimId/files", $multipartContent).Result + $responseBody = $response.Content.ReadAsStringAsync().Result + + if ($response.IsSuccessStatusCode) { + Write-Host " - Uploaded '$fileName' successfully." + } else { + Write-Host " - Failed to upload '$fileName'. HTTP Status: $($response.StatusCode)" + Write-Host " - Error: $responseBody" + $uploadSuccess = $false + } + } catch { + Write-Host " - Failed to upload '$fileName'. Error: $_" + $uploadSuccess = $false + } + } + $httpClient.Dispose() + + # Step 4c: Launch processing + if ($uploadSuccess) { + Write-Host " - Submitting claim batch for processing..." + try { + Invoke-RestMethod -Uri $ClaimProcessorUrl -Method POST ` + -ContentType "application/json" ` + -Body (@{ claim_process_id = $claimId } | ConvertTo-Json) ` + -TimeoutSec 30 -ErrorAction Stop | Out-Null + Write-Host " - Claim batch '$claimId' submitted for processing." + } catch { + Write-Host " - Failed to submit claim batch. Error: $_" + } + } else { + Write-Host " - Skipping batch submission due to upload failures." + } + } + + Write-Host "" + Write-Host ("=" * 60) + Write-Host "Sample file processing completed." + Write-Host ("=" * 60) + } } diff --git a/infra/scripts/post_deployment.sh b/infra/scripts/post_deployment.sh index 2a3e5d0c..bd804150 100644 --- a/infra/scripts/post_deployment.sh +++ b/infra/scripts/post_deployment.sh @@ -237,4 +237,129 @@ else echo "============================================================" echo "Schema registration process completed." echo "============================================================" + + # --- Step 4: Process sample file bundles --- + if [ -n "$SCHEMASET_ID" ] && [ -n "$REGISTERED_IDS" ]; then + echo "" + echo "============================================================" + echo "Step 4: Process sample file bundles" + echo "============================================================" + + SAMPLES_DIR="$(realpath "$SCRIPT_DIR/../../src/ContentProcessorAPI/samples")" + CLAIM_PROCESSOR_URL="$API_BASE_URL/claimprocessor/claims" + + for BUNDLE in claim_date_of_loss claim_hail; do + BUNDLE_DIR="$SAMPLES_DIR/$BUNDLE" + BUNDLE_INFO="$BUNDLE_DIR/bundle_info.json" + + if [ ! -f "$BUNDLE_INFO" ]; then + echo " Skipping '$BUNDLE' - no bundle_info.json found." + continue + fi + + echo "" + echo " 📂 Processing bundle: $BUNDLE" + + # Step 4a: Create claim batch with schemaset ID + echo " - Creating claim batch..." + RESPONSE=$(curl -s -w "\n%{http_code}" \ + -X PUT "$CLAIM_PROCESSOR_URL" \ + -H "Content-Type: application/json" \ + -d "{\"schema_collection_id\": \"$SCHEMASET_ID\"}" \ + --connect-timeout 30) + + HTTP_CODE=$(echo "$RESPONSE" | tail -1) + BODY=$(echo "$RESPONSE" | sed '$d') + + if [ "$HTTP_CODE" != "200" ]; then + echo " ❌ Failed to create claim batch. HTTP $HTTP_CODE" + echo " Error: $BODY" + continue + fi + + CLAIM_ID=$(echo "$BODY" | grep -o '"claim_id"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)"$/\1/') + echo " ✅ Claim batch created with ID: $CLAIM_ID" + + # Step 4b: Upload each file with its mapped schema ID + UPLOAD_SUCCESS=true + FILE_COUNT=$(cat "$BUNDLE_INFO" | grep -o '"file_name"' | wc -l) + + for fidx in $(seq 0 $((FILE_COUNT - 1))); do + FILE_NAME=$(cat "$BUNDLE_INFO" | grep -o '"file_name"[[:space:]]*:[[:space:]]*"[^"]*"' | sed -n "$((fidx + 1))p" | sed 's/.*"\([^"]*\)"$/\1/') + SCHEMA_CLASS=$(cat "$BUNDLE_INFO" | grep -o '"schema_class"[[:space:]]*:[[:space:]]*"[^"]*"' | sed -n "$((fidx + 1))p" | sed 's/.*"\([^"]*\)"$/\1/') + + FILE_PATH="$BUNDLE_DIR/$FILE_NAME" + + if [ ! -f "$FILE_PATH" ]; then + echo " - File '$FILE_NAME' not found. Skipping." + continue + fi + + # Look up schema ID from registered schemas + SCHEMA_ID="" + RIDX=0 + for RID in $REGISTERED_IDS; do + RIDX=$((RIDX + 1)) + RNAME=$(echo "$REGISTERED_NAMES" | tr ' ' '\n' | sed -n "${RIDX}p") + if [ "$RNAME" = "$SCHEMA_CLASS" ]; then + SCHEMA_ID="$RID" + break + fi + done + + if [ -z "$SCHEMA_ID" ]; then + echo " - No schema ID found for '$SCHEMA_CLASS'. Skipping '$FILE_NAME'." + continue + fi + + echo " - Uploading '$FILE_NAME' (schema: $SCHEMA_CLASS)..." + + DATA_JSON="{\"Claim_Id\": \"$CLAIM_ID\", \"Schema_Id\": \"$SCHEMA_ID\", \"Metadata_Id\": \"sample-$BUNDLE\"}" + + RESPONSE=$(curl -s -w "\n%{http_code}" \ + -X POST "$CLAIM_PROCESSOR_URL/$CLAIM_ID/files" \ + -F "data=$DATA_JSON" \ + -F "file=@$FILE_PATH" \ + --connect-timeout 60) + + HTTP_CODE=$(echo "$RESPONSE" | tail -1) + + if [ "$HTTP_CODE" = "200" ]; then + echo " ✅ Uploaded '$FILE_NAME' successfully." + else + BODY=$(echo "$RESPONSE" | sed '$d') + echo " ❌ Failed to upload '$FILE_NAME'. HTTP $HTTP_CODE" + echo " Error: $BODY" + UPLOAD_SUCCESS=false + fi + done + + # Step 4c: Launch processing + if [ "$UPLOAD_SUCCESS" = true ]; then + echo " - Submitting claim batch for processing..." + RESPONSE=$(curl -s -w "\n%{http_code}" \ + -X POST "$CLAIM_PROCESSOR_URL" \ + -H "Content-Type: application/json" \ + -d "{\"claim_process_id\": \"$CLAIM_ID\"}" \ + --connect-timeout 30) + + HTTP_CODE=$(echo "$RESPONSE" | tail -1) + + if [ "$HTTP_CODE" = "202" ]; then + echo " ✅ Claim batch '$CLAIM_ID' submitted for processing." + else + BODY=$(echo "$RESPONSE" | sed '$d') + echo " ❌ Failed to submit claim batch. HTTP $HTTP_CODE" + echo " Error: $BODY" + fi + else + echo " - Skipping batch submission due to upload failures." + fi + done + + echo "" + echo "============================================================" + echo "Sample file processing completed." + echo "============================================================" + fi fi diff --git a/src/ContentProcessorAPI/samples/claim_date_of_loss/bundle_info.json b/src/ContentProcessorAPI/samples/claim_date_of_loss/bundle_info.json new file mode 100644 index 00000000..4ec4f4ac --- /dev/null +++ b/src/ContentProcessorAPI/samples/claim_date_of_loss/bundle_info.json @@ -0,0 +1,8 @@ +{ + "files": [ + { "file_name": "claim_form.pdf", "schema_class": "AutoInsuranceClaimForm" }, + { "file_name": "damage_photo.png", "schema_class": "DamagedVehicleImageAssessment" }, + { "file_name": "police_report.pdf", "schema_class": "PoliceReportDocument" }, + { "file_name": "repair_estimate.pdf", "schema_class": "RepairEstimateDocument" } + ] +} diff --git a/src/ContentProcessorAPI/samples/claim_hail/bundle_info.json b/src/ContentProcessorAPI/samples/claim_hail/bundle_info.json new file mode 100644 index 00000000..dc1dd272 --- /dev/null +++ b/src/ContentProcessorAPI/samples/claim_hail/bundle_info.json @@ -0,0 +1,7 @@ +{ + "files": [ + { "file_name": "claim_form.pdf", "schema_class": "AutoInsuranceClaimForm" }, + { "file_name": "damage_photo.png", "schema_class": "DamagedVehicleImageAssessment" }, + { "file_name": "repair_estimate.pdf", "schema_class": "RepairEstimateDocument" } + ] +}