From 0e6d1933df411947a750206f660478960b120b71 Mon Sep 17 00:00:00 2001 From: Jesus Flores Date: Mon, 11 May 2026 14:49:52 -0600 Subject: [PATCH 1/2] [DMS-912] Update DmsApi SDKs to net10.0 via OpenAPI Generator 7.19.0 --- .github/workflows/scheduled-smoke-test.yml | 6 +- .gitignore | 2 +- build-sdk.ps1 | 78 ++++++++++++++++++++-- eng/sdkGen/EdFi.DmsApi.Sdk.nuspec | 12 ++-- eng/sdkGen/EdFi.DmsApi.TestSdk.nuspec | 12 ++-- 5 files changed, 87 insertions(+), 23 deletions(-) diff --git a/.github/workflows/scheduled-smoke-test.yml b/.github/workflows/scheduled-smoke-test.yml index e98a14d4f1..edee04c52a 100644 --- a/.github/workflows/scheduled-smoke-test.yml +++ b/.github/workflows/scheduled-smoke-test.yml @@ -130,7 +130,7 @@ jobs: - name: Verify SDK Generation run: | Write-Host "Checking SDK generation results..." - $sdkPath = "${{ github.workspace }}/eng/sdkGen/csharp/src/EdFi.DmsApi.TestSdk/bin/Release/net8.0/EdFi.DmsApi.TestSdk.dll" + $sdkPath = "${{ github.workspace }}/eng/sdkGen/csharp/src/EdFi.DmsApi.TestSdk/bin/Release/net10.0/EdFi.DmsApi.TestSdk.dll" Write-Host "Expected SDK path: $sdkPath" if (Test-Path $sdkPath) { @@ -158,12 +158,12 @@ jobs: - name: Run NonDestructive DMS SDK Tests run: | - ./Invoke-NonDestructiveSdkTests.ps1 -BaseUrl "http://localhost:8080" -Key "$env:SMOKE_TEST_KEY" -Secret "$env:SMOKE_TEST_SECRET" -SdkPath "${{ github.workspace }}/eng/sdkGen/csharp/src/EdFi.DmsApi.TestSdk/bin/Release/net8.0/EdFi.DmsApi.TestSdk.dll" + ./Invoke-NonDestructiveSdkTests.ps1 -BaseUrl "http://localhost:8080" -Key "$env:SMOKE_TEST_KEY" -Secret "$env:SMOKE_TEST_SECRET" -SdkPath "${{ github.workspace }}/eng/sdkGen/csharp/src/EdFi.DmsApi.TestSdk/bin/Release/net10.0/EdFi.DmsApi.TestSdk.dll" working-directory: eng/smoke_test/ - name: Run Destructive DMS SDK Tests run: | - ./Invoke-DestructiveSdkTests.ps1 -BaseUrl "http://localhost:8080" -Key "$env:SMOKE_TEST_KEY" -Secret "$env:SMOKE_TEST_SECRET" -SdkPath "${{ github.workspace }}/eng/sdkGen/csharp/src/EdFi.DmsApi.TestSdk/bin/Release/net8.0/EdFi.DmsApi.TestSdk.dll" + ./Invoke-DestructiveSdkTests.ps1 -BaseUrl "http://localhost:8080" -Key "$env:SMOKE_TEST_KEY" -Secret "$env:SMOKE_TEST_SECRET" -SdkPath "${{ github.workspace }}/eng/sdkGen/csharp/src/EdFi.DmsApi.TestSdk/bin/Release/net10.0/EdFi.DmsApi.TestSdk.dll" working-directory: eng/smoke_test/ - name: Run NonDestructive ODS SDK Tests diff --git a/.gitignore b/.gitignore index 95ff05a747..772e4665a6 100644 --- a/.gitignore +++ b/.gitignore @@ -446,7 +446,7 @@ CLAUDE.local.md # SDK generation artifacts /eng/sdkGen/csharp/* -openApi-codegen-cli.jar +openApi-codegen-cli-*.jar /.github/copilot-instructions.md # Claude temporary files leaking into the project root in 2.1.5 diff --git a/build-sdk.ps1 b/build-sdk.ps1 index 703faa8d52..a845b0ddcc 100644 --- a/build-sdk.ps1 +++ b/build-sdk.ps1 @@ -77,13 +77,16 @@ param( Import-Module -Name "$PSScriptRoot/eng/build-helpers.psm1" -Force +$openApiGeneratorVersion = "7.19.0" +$openApiGeneratorJar = "openApi-codegen-cli-$openApiGeneratorVersion.jar" + $solutionRoot = "$PSScriptRoot/$OutputFolder" $projectPath = "$solutionRoot/src/$PackageName/$PackageName.csproj" $nuspecPath = "$PSScriptRoot/eng/sdkGen/$PackageName.nuspec" function DownloadCodeGen { - if (-not (Test-Path -Path openApi-codegen-cli.jar)) { - Invoke-WebRequest -OutFile openApi-codegen-cli.jar https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.9.0/openapi-generator-cli-7.9.0.jar + if (-not (Test-Path -Path $openApiGeneratorJar)) { + Invoke-WebRequest -OutFile $openApiGeneratorJar https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/$openApiGeneratorVersion/openapi-generator-cli-$openApiGeneratorVersion.jar } } @@ -96,7 +99,10 @@ function GenerateSdk { $ModelPackage, [string] - $Endpoint + $Endpoint, + + [string] + $ExtraOperationIdMappings = "" ) # Download and parse OpenAPI spec @@ -126,14 +132,23 @@ function GenerateSdk { $mappings = ($operationIds | Sort-Object -Unique | ForEach-Object { "$(Normalize-OperationId $_)=$(Capitalize-FirstChar $_)" }) -join "," # Example --operation-id-name-mappings deleteHomographContactsById=Delete_HomographContactsById - & java -Xmx5g -jar openApi-codegen-cli.jar generate ` + # Merge in any extra mappings provided by the caller (e.g. to resolve cross-spec operationId collisions) + if ($ExtraOperationIdMappings -ne "") { + if ($mappings -ne "") { + $mappings = "$mappings,$ExtraOperationIdMappings" + } else { + $mappings = $ExtraOperationIdMappings + } + } + + & java -Xmx5g -jar $openApiGeneratorJar generate ` -g csharp ` -i $Endpoint ` --api-package $ApiPackage ` --model-package $ModelPackage ` -o $OutputFolder ` --operation-id-name-mappings $mappings ` - --additional-properties "packageName=$PackageName,targetFramework=net8.0,netCoreProjectFile=true" ` + --additional-properties "packageName=$PackageName,targetFramework=net10.0,netCoreProjectFile=true" ` --global-property modelTests=false ` --global-property apiTests=false ` --global-property apiDocs=false ` @@ -195,15 +210,64 @@ function PushPackage { } } +function Get-OperationIds { + param([string]$Endpoint) + $spec = Invoke-WebRequest -Uri $Endpoint | ConvertFrom-Json + $ids = $spec.paths.PSObject.Properties.Value | ForEach-Object { + $_.PSObject.Properties.Value | Where-Object { $_.operationId } | ForEach-Object { $_.operationId } + } + return ($ids | Sort-Object -Unique) +} + +function Build-CollisionMappings { + param( + [string[]]$ResourcesIds, + [string[]]$DescriptorsIds + ) + # Find operationIds present in both specs (collisions that would produce duplicate C# names) + $collisions = $DescriptorsIds | Where-Object { $ResourcesIds -contains $_ } + if ($collisions.Count -eq 0) { + return "" + } + # For each colliding descriptor operationId, insert "Descriptor" before any "ById" suffix + # or replace a trailing "s" with "Descriptors", otherwise append "Descriptor". + # Examples: getGradingPeriods -> getGradingPeriodDescriptors + # deleteGradingPeriodsById -> deleteGradingPeriodDescriptorsById + # postGradingPeriod -> postGradingPeriodDescriptor + # putGradingPeriod -> putGradingPeriodDescriptor + $mappings = $collisions | ForEach-Object { + $old = $_ + if ($old -match 'ById$') { + $new = $old -replace 'ById$', 'DescriptorById' + } elseif ($old -match 's$') { + $new = $old -replace 's$', 'Descriptors' + } else { + $new = "${old}Descriptor" + } + "$old=$new" + } + return ($mappings -join ",") +} + function Invoke-BuildAndGenerateSdk { Invoke-Step { DownloadCodeGen } if ($PackageName -eq "EdFi.DmsApi.TestSdk") { Invoke-Step { GenerateSdk -ApiPackage "Apis.All" -ModelPackage "Models.All" -Endpoint "$DmsUrl/metadata/specifications/resources-spec.json" } - Invoke-Step { GenerateSdk -ApiPackage "Apis.All" -ModelPackage "Models.All" -Endpoint "$DmsUrl/metadata/specifications/descriptors-spec.json" } + # Detect operationId collisions between the two specs and remap the descriptor-side ones to + # avoid duplicate C# type names when both specs are merged into the same Apis.All namespace. + $resourcesIds = Get-OperationIds -Endpoint "$DmsUrl/metadata/specifications/resources-spec.json" + $descriptorsIds = Get-OperationIds -Endpoint "$DmsUrl/metadata/specifications/descriptors-spec.json" + $collisionMappings = Build-CollisionMappings -ResourcesIds $resourcesIds -DescriptorsIds $descriptorsIds + Invoke-Step { GenerateSdk -ApiPackage "Apis.All" -ModelPackage "Models.All" -Endpoint "$DmsUrl/metadata/specifications/descriptors-spec.json" -ExtraOperationIdMappings $collisionMappings } } elseif ($PackageName -eq "EdFi.DmsApi.Sdk") { Invoke-Step { GenerateSdk -ApiPackage "Apis.Ed_Fi" -ModelPackage "Models.Ed_Fi" -Endpoint "$DmsUrl/metadata/specifications/resources-spec.json" } - Invoke-Step { GenerateSdk -ApiPackage "Apis.Ed_Fi" -ModelPackage "Models.Ed_Fi" -Endpoint "$DmsUrl/metadata/specifications/descriptors-spec.json" } + # Apply the same collision-avoidance logic as TestSdk so that GradingPeriodDescriptors + # response interfaces do not duplicate those from the GradingPeriods resources spec. + $resourcesIds = Get-OperationIds -Endpoint "$DmsUrl/metadata/specifications/resources-spec.json" + $descriptorsIds = Get-OperationIds -Endpoint "$DmsUrl/metadata/specifications/descriptors-spec.json" + $collisionMappings = Build-CollisionMappings -ResourcesIds $resourcesIds -DescriptorsIds $descriptorsIds + Invoke-Step { GenerateSdk -ApiPackage "Apis.Ed_Fi" -ModelPackage "Models.Ed_Fi" -Endpoint "$DmsUrl/metadata/specifications/descriptors-spec.json" -ExtraOperationIdMappings $collisionMappings } } else { throw "Unknown PackageName value: $PackageName" } diff --git a/eng/sdkGen/EdFi.DmsApi.Sdk.nuspec b/eng/sdkGen/EdFi.DmsApi.Sdk.nuspec index 72bfa53eb7..3dde58d432 100644 --- a/eng/sdkGen/EdFi.DmsApi.Sdk.nuspec +++ b/eng/sdkGen/EdFi.DmsApi.Sdk.nuspec @@ -13,16 +13,16 @@ Copyright @ $year$ Ed-Fi Alliance, LLC and Contributors Ed-Fi Data Management Service SDK - - - - - + + + + + - + diff --git a/eng/sdkGen/EdFi.DmsApi.TestSdk.nuspec b/eng/sdkGen/EdFi.DmsApi.TestSdk.nuspec index ea2815e724..71c6a480ee 100644 --- a/eng/sdkGen/EdFi.DmsApi.TestSdk.nuspec +++ b/eng/sdkGen/EdFi.DmsApi.TestSdk.nuspec @@ -13,16 +13,16 @@ Copyright @ $year$ Ed-Fi Alliance, LLC and Contributors Ed-Fi Data Management Service TestSdk - - - - - + + + + + - + From a0d651bc3627b09b094f62edf0e6375f345fb95d Mon Sep 17 00:00:00 2001 From: Jesus Flores Date: Wed, 13 May 2026 17:24:55 -0600 Subject: [PATCH 2/2] [DMS-912] Enable smoke test suites against local DMS stack --- build-sdk.ps1 | 250 +++++++++++------- eng/Package-Management.psm1 | 12 +- eng/smoke_test/Invoke-DestructiveSdkTests.ps1 | 2 +- .../Invoke-NonDestructiveApiTests.ps1 | 2 +- .../Invoke-NonDestructiveSdkTests.ps1 | 2 +- eng/smoke_test/modules/SmokeTest.psm1 | 35 ++- .../Modules/CoreEndpointModule.cs | 15 +- 7 files changed, 206 insertions(+), 112 deletions(-) diff --git a/build-sdk.ps1 b/build-sdk.ps1 index a845b0ddcc..7729893880 100644 --- a/build-sdk.ps1 +++ b/build-sdk.ps1 @@ -90,71 +90,164 @@ function DownloadCodeGen { } } -function GenerateSdk { - param ( - [string] - $ApiPackage, +function Rename-DescriptorOperationId { + param([string]$old) + if ($old -match 'ById$') { + return ($old -replace 'ById$', 'DescriptorById') + } elseif ($old -match 's$') { + return ($old -replace 's$', 'Descriptors') + } else { + return "${old}Descriptor" + } +} - [string] - $ModelPackage, +function Merge-DmsSpecs { + # Merges resources-spec and descriptors-spec into a single OpenAPI document so the generator + # runs once and emits a complete HostConfiguration.cs / IApi.cs. The two-pass flow used to + # let the descriptors pass clobber the resources-pass HostConfiguration, leaving resource + # APIs unregistered in DI and the smoke test tool throwing NREs on every resource call. + param( + [string]$ResourcesEndpoint, + [string]$DescriptorsEndpoint + ) - [string] - $Endpoint, + $resources = (Invoke-WebRequest -Uri $ResourcesEndpoint).Content | ConvertFrom-Json -Depth 100 -AsHashtable + $descriptors = (Invoke-WebRequest -Uri $DescriptorsEndpoint).Content | ConvertFrom-Json -Depth 100 -AsHashtable + + # Rename descriptor operationIds that collide with resource ones (e.g. getGradingPeriods + # appears in both specs; without renaming we'd get duplicate C# method names in Apis.All). + $resourceIds = @{} + foreach ($pathOps in $resources.paths.Values) { + foreach ($op in $pathOps.Values) { + if ($op -is [System.Collections.IDictionary] -and $op.ContainsKey('operationId')) { + $resourceIds[$op.operationId] = $true + } + } + } + foreach ($pathOps in $descriptors.paths.Values) { + foreach ($op in $pathOps.Values) { + if ($op -is [System.Collections.IDictionary] -and $op.ContainsKey('operationId') -and $resourceIds.ContainsKey($op.operationId)) { + $op.operationId = Rename-DescriptorOperationId $op.operationId + } + } + } - [string] - $ExtraOperationIdMappings = "" + # Union descriptor paths/schemas/tags into resources. Parameters/responses/securitySchemes + # are identical across both specs (verified by inspection) so the resources copy is kept. + foreach ($pathName in $descriptors.paths.Keys) { + if (-not $resources.paths.ContainsKey($pathName)) { + $resources.paths[$pathName] = $descriptors.paths[$pathName] + } + } + foreach ($schemaName in $descriptors.components.schemas.Keys) { + if (-not $resources.components.schemas.ContainsKey($schemaName)) { + $resources.components.schemas[$schemaName] = $descriptors.components.schemas[$schemaName] + } + } + $existingTagNames = @{} + foreach ($t in $resources.tags) { $existingTagNames[$t.name] = $true } + foreach ($t in $descriptors.tags) { + if (-not $existingTagNames.ContainsKey($t.name)) { + $resources.tags += $t + } + } + + # Drop required arrays on Homograph* schemas so the generichost-library generator omits its + # throw-on-missing-required validation. The smoke test tool's data factory cannot populate + # required props on these extension models, and the server-side spec still enforces them. + foreach ($schemaName in @($resources.components.schemas.Keys)) { + if ($schemaName.StartsWith('Homograph')) { + $schema = $resources.components.schemas[$schemaName] + if ($schema -is [System.Collections.IDictionary] -and $schema.ContainsKey('required')) { + $schema.Remove('required') + } + } + } + + return $resources +} + +function GenerateSdk { + param ( + [string]$ApiPackage, + [string]$ModelPackage, + [System.Collections.IDictionary]$Spec ) - # Download and parse OpenAPI spec - $spec = Invoke-WebRequest -Uri $Endpoint | ConvertFrom-Json + # Rewrite tags on non-ed-fi paths whose tag also appears on /ed-fi/* paths. Without this, + # /ed-fi/contacts and /homograph/contacts share tag 'contacts' and land on the same + # ContactsApi, which the smoke test tool's "one Post per Api class" categorizer can't + # disambiguate. The generator emits a distinct *Api class per tag, so prefixing the tag + # with the namespace splits the colliding endpoints into separate classes. + $coreTags = @{} + foreach ($pathName in @($Spec.paths.Keys)) { + if ($pathName.StartsWith('/ed-fi/')) { + foreach ($verb in @($Spec.paths[$pathName].Keys)) { + $op = $Spec.paths[$pathName][$verb] + if ($op -is [System.Collections.IDictionary] -and $op.ContainsKey('tags') -and $op['tags']) { + foreach ($t in $op['tags']) { $coreTags[$t] = $true } + } + } + } + } + foreach ($pathName in @($Spec.paths.Keys)) { + if ($pathName -match '^/(?[^/]+)/' -and $matches.ns -ne 'ed-fi') { + $nsSafe = ($matches.ns -replace '[^A-Za-z0-9]', '') + foreach ($verb in @($Spec.paths[$pathName].Keys)) { + $op = $Spec.paths[$pathName][$verb] + if ($op -is [System.Collections.IDictionary] -and $op.ContainsKey('tags') -and $op['tags']) { + $op['tags'] = @($op['tags'] | ForEach-Object { if ($coreTags.ContainsKey($_)) { "${nsSafe}_$_" } else { $_ } }) + } + } + } + } - # Find all operationIds that contain an underscore - $operationIds = $spec.paths.PSObject.Properties.Value | ForEach-Object { - $_.PSObject.Properties.Value | Where-Object { $_.operationId -and $_.operationId -like "*_*" } | ForEach-Object { $_.operationId } + # Build --operation-id-name-mappings for underscore-bearing operationIds so the generator + # preserves the namespace separator in C# method names (post_HomographContact stays + # Post_HomographContact rather than collapsing to PostHomographContact). + $operationIds = @() + foreach ($pathOps in $Spec.paths.Values) { + foreach ($op in $pathOps.Values) { + if ($op -is [System.Collections.IDictionary] -and $op.ContainsKey('operationId') -and $op.operationId -like "*_*") { + $operationIds += $op.operationId + } + } } - # Normalize operationId to camelCase without underscores (for the left side of mapping) function Normalize-OperationId { param($opId) $parts = $opId -split '_' - $camel = $parts[0] + ($parts[1..($parts.Count-1)] | ForEach-Object { $_.Substring(0,1).ToUpper() + $_.Substring(1) } | ForEach-Object { $_ }) -join '' + $camel = $parts[0] + (($parts[1..($parts.Count-1)] | ForEach-Object { $_.Substring(0,1).ToUpper() + $_.Substring(1) }) -join '') return $camel } - - # Capitalize the first character of the string function Capitalize-FirstChar { param($s) if ($s.Length -eq 0) { return $s } return $s.Substring(0,1).ToUpper() + $s.Substring(1) } - - # Build mappings string: left = normalized, right = original with first char uppercased $mappings = ($operationIds | Sort-Object -Unique | ForEach-Object { "$(Normalize-OperationId $_)=$(Capitalize-FirstChar $_)" }) -join "," - # Example --operation-id-name-mappings deleteHomographContactsById=Delete_HomographContactsById - - # Merge in any extra mappings provided by the caller (e.g. to resolve cross-spec operationId collisions) - if ($ExtraOperationIdMappings -ne "") { - if ($mappings -ne "") { - $mappings = "$mappings,$ExtraOperationIdMappings" - } else { - $mappings = $ExtraOperationIdMappings - } - } - - & java -Xmx5g -jar $openApiGeneratorJar generate ` - -g csharp ` - -i $Endpoint ` - --api-package $ApiPackage ` - --model-package $ModelPackage ` - -o $OutputFolder ` - --operation-id-name-mappings $mappings ` - --additional-properties "packageName=$PackageName,targetFramework=net10.0,netCoreProjectFile=true" ` - --global-property modelTests=false ` - --global-property apiTests=false ` - --global-property apiDocs=false ` - --global-property modelDocs=false ` - --skip-validate-spec + $specTempPath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "openapi-spec-$([System.Guid]::NewGuid()).json") + $Spec | ConvertTo-Json -Depth 100 -Compress | Set-Content -Path $specTempPath -Encoding UTF8NoBOM + + try { + & java -Xmx5g -jar $openApiGeneratorJar generate ` + -g csharp ` + -i $specTempPath ` + --api-package $ApiPackage ` + --model-package $ModelPackage ` + -o $OutputFolder ` + --operation-id-name-mappings $mappings ` + --additional-properties "packageName=$PackageName,targetFramework=net10.0,netCoreProjectFile=true" ` + --global-property modelTests=false ` + --global-property apiTests=false ` + --global-property apiDocs=false ` + --global-property modelDocs=false ` + --skip-validate-spec + } + finally { + Remove-Item $specTempPath -ErrorAction SilentlyContinue + } } function BuildSdk { @@ -210,68 +303,21 @@ function PushPackage { } } -function Get-OperationIds { - param([string]$Endpoint) - $spec = Invoke-WebRequest -Uri $Endpoint | ConvertFrom-Json - $ids = $spec.paths.PSObject.Properties.Value | ForEach-Object { - $_.PSObject.Properties.Value | Where-Object { $_.operationId } | ForEach-Object { $_.operationId } - } - return ($ids | Sort-Object -Unique) -} - -function Build-CollisionMappings { - param( - [string[]]$ResourcesIds, - [string[]]$DescriptorsIds - ) - # Find operationIds present in both specs (collisions that would produce duplicate C# names) - $collisions = $DescriptorsIds | Where-Object { $ResourcesIds -contains $_ } - if ($collisions.Count -eq 0) { - return "" - } - # For each colliding descriptor operationId, insert "Descriptor" before any "ById" suffix - # or replace a trailing "s" with "Descriptors", otherwise append "Descriptor". - # Examples: getGradingPeriods -> getGradingPeriodDescriptors - # deleteGradingPeriodsById -> deleteGradingPeriodDescriptorsById - # postGradingPeriod -> postGradingPeriodDescriptor - # putGradingPeriod -> putGradingPeriodDescriptor - $mappings = $collisions | ForEach-Object { - $old = $_ - if ($old -match 'ById$') { - $new = $old -replace 'ById$', 'DescriptorById' - } elseif ($old -match 's$') { - $new = $old -replace 's$', 'Descriptors' - } else { - $new = "${old}Descriptor" - } - "$old=$new" - } - return ($mappings -join ",") -} - function Invoke-BuildAndGenerateSdk { Invoke-Step { DownloadCodeGen } - if ($PackageName -eq "EdFi.DmsApi.TestSdk") { - Invoke-Step { GenerateSdk -ApiPackage "Apis.All" -ModelPackage "Models.All" -Endpoint "$DmsUrl/metadata/specifications/resources-spec.json" } - # Detect operationId collisions between the two specs and remap the descriptor-side ones to - # avoid duplicate C# type names when both specs are merged into the same Apis.All namespace. - $resourcesIds = Get-OperationIds -Endpoint "$DmsUrl/metadata/specifications/resources-spec.json" - $descriptorsIds = Get-OperationIds -Endpoint "$DmsUrl/metadata/specifications/descriptors-spec.json" - $collisionMappings = Build-CollisionMappings -ResourcesIds $resourcesIds -DescriptorsIds $descriptorsIds - Invoke-Step { GenerateSdk -ApiPackage "Apis.All" -ModelPackage "Models.All" -Endpoint "$DmsUrl/metadata/specifications/descriptors-spec.json" -ExtraOperationIdMappings $collisionMappings } - } elseif ($PackageName -eq "EdFi.DmsApi.Sdk") { - Invoke-Step { GenerateSdk -ApiPackage "Apis.Ed_Fi" -ModelPackage "Models.Ed_Fi" -Endpoint "$DmsUrl/metadata/specifications/resources-spec.json" } - # Apply the same collision-avoidance logic as TestSdk so that GradingPeriodDescriptors - # response interfaces do not duplicate those from the GradingPeriods resources spec. - $resourcesIds = Get-OperationIds -Endpoint "$DmsUrl/metadata/specifications/resources-spec.json" - $descriptorsIds = Get-OperationIds -Endpoint "$DmsUrl/metadata/specifications/descriptors-spec.json" - $collisionMappings = Build-CollisionMappings -ResourcesIds $resourcesIds -DescriptorsIds $descriptorsIds - Invoke-Step { GenerateSdk -ApiPackage "Apis.Ed_Fi" -ModelPackage "Models.Ed_Fi" -Endpoint "$DmsUrl/metadata/specifications/descriptors-spec.json" -ExtraOperationIdMappings $collisionMappings } - } else { - throw "Unknown PackageName value: $PackageName" + $mergedSpec = Merge-DmsSpecs ` + -ResourcesEndpoint "$DmsUrl/metadata/specifications/resources-spec.json" ` + -DescriptorsEndpoint "$DmsUrl/metadata/specifications/descriptors-spec.json" + + $packagePair = switch ($PackageName) { + "EdFi.DmsApi.TestSdk" { @{ Api = "Apis.All"; Model = "Models.All" } } + "EdFi.DmsApi.Sdk" { @{ Api = "Apis.Ed_Fi"; Model = "Models.Ed_Fi" } } + default { throw "Unknown PackageName value: $PackageName" } } + Invoke-Step { GenerateSdk -ApiPackage $packagePair.Api -ModelPackage $packagePair.Model -Spec $mergedSpec } + Invoke-Step { BuildSdk } } diff --git a/eng/Package-Management.psm1 b/eng/Package-Management.psm1 index 042666ed10..7c86beeba8 100644 --- a/eng/Package-Management.psm1 +++ b/eng/Package-Management.psm1 @@ -123,6 +123,16 @@ function Get-NugetPackage { $PreRelease ) + $lowerId = $PackageName.ToLower() + $packagesDir = ".packages" + + if (-not [string]::IsNullOrWhiteSpace($PackageVersion) -and $PackageVersion.Split('.').Length -ge 3) { + $cachedPackage = Join-Path -Path $packagesDir -ChildPath "$lowerId.$PackageVersion" + if (Test-Path -Path $cachedPackage) { + return $cachedPackage + } + } + # Pre-releases $nugetServicesURL = $ReleaseServiceIndex if ($PreRelease) { @@ -136,7 +146,6 @@ function Get-NugetPackage { | Where-Object { $_."@type" -like "PackageBaseAddress*" } ` | Select-Object -Property "@id" -ExpandProperty "@id" - $lowerId = $PackageName.ToLower() # Lookup available packages $package = Invoke-RestMethod "$($packageService)$($lowerId)/index.json" # Sort by SemVer @@ -165,7 +174,6 @@ function Get-NugetPackage { $file = "$($lowerId).$($version)" $zip = "$($file).zip" - $packagesDir = ".packages" New-Item -Path $packagesDir -Force -ItemType Directory | Out-Null Push-Location $packagesDir diff --git a/eng/smoke_test/Invoke-DestructiveSdkTests.ps1 b/eng/smoke_test/Invoke-DestructiveSdkTests.ps1 index bc91232a10..7585585c1a 100644 --- a/eng/smoke_test/Invoke-DestructiveSdkTests.ps1 +++ b/eng/smoke_test/Invoke-DestructiveSdkTests.ps1 @@ -40,7 +40,7 @@ if ($SdkPath) { $sdkDllPath = Get-ApiSdkDll } -$path = Get-SmokeTestTool -PackageVersion '7.3.10008' -PreRelease +$path = Get-SmokeTestTool -PackageVersion '7.3.20144' -PreRelease $parameters = @{ BaseUrl = $BaseUrl diff --git a/eng/smoke_test/Invoke-NonDestructiveApiTests.ps1 b/eng/smoke_test/Invoke-NonDestructiveApiTests.ps1 index 52c46b78ee..1c33e50ee4 100644 --- a/eng/smoke_test/Invoke-NonDestructiveApiTests.ps1 +++ b/eng/smoke_test/Invoke-NonDestructiveApiTests.ps1 @@ -23,7 +23,7 @@ $ErrorActionPreference = "Stop" Import-Module ../Package-Management.psm1 -Force Import-Module ./modules/SmokeTest.psm1 -$path = Get-SmokeTestTool -PackageVersion '7.2.413' +$path = Get-SmokeTestTool -PackageVersion '7.3.20144' -PreRelease $parameters = @{ BaseUrl = $BaseUrl diff --git a/eng/smoke_test/Invoke-NonDestructiveSdkTests.ps1 b/eng/smoke_test/Invoke-NonDestructiveSdkTests.ps1 index 42dea53325..b163db8817 100644 --- a/eng/smoke_test/Invoke-NonDestructiveSdkTests.ps1 +++ b/eng/smoke_test/Invoke-NonDestructiveSdkTests.ps1 @@ -40,7 +40,7 @@ if ($SdkPath) { $sdkDllPath = Get-ApiSdkDll } -$path = Get-SmokeTestTool -PackageVersion '7.3.10008' -PreRelease +$path = Get-SmokeTestTool -PackageVersion '7.3.20144' -PreRelease $parameters = @{ BaseUrl = $BaseUrl diff --git a/eng/smoke_test/modules/SmokeTest.psm1 b/eng/smoke_test/modules/SmokeTest.psm1 index 3baebb5f42..a2aea66f8c 100644 --- a/eng/smoke_test/modules/SmokeTest.psm1 +++ b/eng/smoke_test/modules/SmokeTest.psm1 @@ -9,6 +9,31 @@ $ErrorActionPreference = "Stop" # Import the Dms-Management module for vendor/application management Import-Module "$PSScriptRoot/../../Dms-Management.psm1" -Force +function Get-SmokeTestConsolePath { + param ( + [string] + [Parameter(Mandatory = $True)] + $ToolPath + ) + + $toolsPath = Join-Path -Path ($ToolPath).Trim() -ChildPath "tools" + $frameworkDirectory = Get-ChildItem -Path $toolsPath -Directory | + Where-Object { $_.Name -match '^net\d+(\.\d+)?$' } | + Sort-Object { [version]($_.Name -replace '^net', '') } -Descending | + Select-Object -First 1 + + if ($null -eq $frameworkDirectory) { + throw "No supported .NET tool framework directory found in '$toolsPath'." + } + + $consolePath = Join-Path -Path $frameworkDirectory.FullName -ChildPath "any/EdFi.SmokeTest.Console.dll" + if (-not (Test-Path $consolePath)) { + throw "Smoke test console not found at '$consolePath'." + } + + return $consolePath +} + function Invoke-SmokeTestUtility { [CmdletBinding()] @@ -36,13 +61,21 @@ function Invoke-SmokeTestUtility { [string] $SdkPath, + [string] + $OAuthUrl, + [ValidateSet("EdFi.DmsApi.TestSdk", "EdFi.DmsApi.Sdk", "EdFi.OdsApi.Sdk")] [string] $SdkNamespace = "EdFi.DmsApi.TestSdk" ) + if ([string]::IsNullOrWhiteSpace($OAuthUrl)) { + $OAuthUrl = "$($BaseUrl.TrimEnd('/'))/oauth/token" + } + $options = @( "-b", $BaseUrl, + "-o", $OAuthUrl, "-k", $Key, "-s", $Secret, "-t", $TestSet, @@ -64,7 +97,7 @@ function Invoke-SmokeTestUtility { Write-Output $ToolPath $options $host.UI.RawUI.ForegroundColor = $previousForegroundColor - $path = (Join-Path -Path ($ToolPath).Trim() -ChildPath "tools/net8.0/any/EdFi.SmokeTest.Console.dll") + $path = Get-SmokeTestConsolePath -ToolPath $ToolPath &dotnet $path $options } diff --git a/src/dms/frontend/EdFi.DataManagementService.Frontend.AspNetCore/Modules/CoreEndpointModule.cs b/src/dms/frontend/EdFi.DataManagementService.Frontend.AspNetCore/Modules/CoreEndpointModule.cs index 3e28237be9..4eac8566aa 100644 --- a/src/dms/frontend/EdFi.DataManagementService.Frontend.AspNetCore/Modules/CoreEndpointModule.cs +++ b/src/dms/frontend/EdFi.DataManagementService.Frontend.AspNetCore/Modules/CoreEndpointModule.cs @@ -19,10 +19,17 @@ public void MapEndpoints(IEndpointRouteBuilder endpoints) appSettings.Value.MultiTenancy ); - endpoints.MapPost(routePattern, Upsert); - endpoints.MapGet(routePattern, Get); - endpoints.MapPut(routePattern, UpdateById); - endpoints.MapDelete(routePattern, DeleteById); + // /data/v3 alias for parity with ODS API URLs so SDK consumers (e.g. EdFi.LoadTools' + // SdkConfigurationFactory) that hardcode the historical /data/v3 base reach the same handlers. + string v3AliasPattern = routePattern.Replace("/data/{**dmsPath}", "/data/v3/{**dmsPath}"); + + foreach (var pattern in new[] { routePattern, v3AliasPattern }) + { + endpoints.MapPost(pattern, Upsert); + endpoints.MapGet(pattern, Get); + endpoints.MapPut(pattern, UpdateById); + endpoints.MapDelete(pattern, DeleteById); + } } ///