diff --git a/src/Refitter.Core/RefitGenerator.cs b/src/Refitter.Core/RefitGenerator.cs index d36addebb..1dd333adf 100644 --- a/src/Refitter.Core/RefitGenerator.cs +++ b/src/Refitter.Core/RefitGenerator.cs @@ -9,6 +9,11 @@ namespace Refitter.Core; /// public class RefitGenerator(RefitGeneratorSettings settings, OpenApiDocument document) { + /// + /// OpenAPI specifications used to generate Refit clients and interfaces. + /// + public OpenApiDocument OpenApiDocument => document; + /// /// Creates a new instance of the class asynchronously. /// diff --git a/src/Refitter/GenerateCommand.cs b/src/Refitter/GenerateCommand.cs index 2fdbcd39d..e2bfa6f46 100644 --- a/src/Refitter/GenerateCommand.cs +++ b/src/Refitter/GenerateCommand.cs @@ -71,7 +71,30 @@ public override async Task ExecuteAsync(CommandContext context, Settings se await (refitGeneratorSettings.GenerateMultipleFiles ? WriteMultipleFiles(generator, settings, refitGeneratorSettings) - : WriteSingleFile(generator, settings, refitGeneratorSettings)); Analytics.LogFeatureUsage(settings, refitGeneratorSettings); + : WriteSingleFile(generator, settings, refitGeneratorSettings)); + + Analytics.LogFeatureUsage(settings, refitGeneratorSettings); + + if (refitGeneratorSettings.IncludePathMatches.Length > 0 && + generator.OpenApiDocument.Paths.Count == 0) + { + Console.WriteLine("⚠️ WARNING: All API paths were filtered out by --match-path patterns. ⚠️"); + Console.WriteLine($" Match Patterns used: {string.Join(", ", refitGeneratorSettings.IncludePathMatches)}"); + Console.WriteLine(); + Console.WriteLine(" This could indicate that:"); + Console.WriteLine(" 1. The regex patterns don't match any available paths"); + Console.WriteLine(" 2. There's a syntax error in the regex patterns"); + Console.WriteLine(" 3. The patterns were corrupted by command line interpretation"); + Console.WriteLine(); + Console.WriteLine(" This commonly happens when using the Windows Command Prompt (CMD) instead of PowerShell."); + Console.WriteLine(" The ^ character in regex patterns is interpreted as an escape character in CMD."); + Console.WriteLine(); + Console.WriteLine(" Solutions:"); + Console.WriteLine(" 1. Use PowerShell instead of CMD"); + Console.WriteLine(" 2. In CMD, escape the ^ character or use different quoting"); + Console.WriteLine(" 3. Use a .refitter settings file instead of command line arguments"); + Console.WriteLine(); + } // Success summary with performance metrics stopwatch.Stop(); diff --git a/test/smoke-tests.bat b/test/smoke-tests.bat new file mode 100644 index 000000000..07863416e --- /dev/null +++ b/test/smoke-tests.bat @@ -0,0 +1,215 @@ +@echo off +setlocal enabledelayedexpansion + +:: Parse command line arguments +set "PARALLEL=true" +set "USE_PRODUCTION=false" + +:parse_args +if "%~1"=="" goto :main +if /i "%~1"=="-parallel" ( + set "PARALLEL=%~2" + shift + shift + goto :parse_args +) +if /i "%~1"=="-useproduction" ( + set "USE_PRODUCTION=true" + shift + goto :parse_args +) +shift +goto :parse_args + +:main +echo Parallel: %PARALLEL% +echo UseProduction: %USE_PRODUCTION% + +goto :run_tests + +:throw_on_native_failure +if not %errorlevel%==0 ( + echo Native Failure + exit /b 1 +) +goto :eof + +:generate_and_build +set "format=%~1" +set "namespace=%~2" +set "output_path=%~3" +set "args=%~4" +set "net_core=%~5" +set "csproj=%~6" +set "build_from_source=%~7" + +if "%build_from_source%"=="" set "build_from_source=true" + +:: Clean up generated files +del /q /s ".\GeneratedCode\*.cs" 2>nul + +set "process_path=.\bin\refitter" +if /i "%build_from_source%"=="false" set "process_path=refitter" + +:: Check if using settings file +echo %args% | findstr "settings-file" >nul +if %errorlevel%==0 ( + echo %process_path% --no-logging %args% + %process_path% --no-logging %args% + call :throw_on_native_failure +) else ( + echo %process_path% .\openapi.%format% --namespace %namespace% --output .\GeneratedCode\%output_path% --no-logging %args% + %process_path% .\openapi.%format% --namespace %namespace% --output .\GeneratedCode\%output_path% --no-logging %args% + call :throw_on_native_failure +) + +:: Build the project +if not "%csproj%"=="" ( + echo. + echo Building %csproj% file + echo. + set "solution=%csproj%" +) else ( + echo. + echo Building ConsoleApp + echo. + set "solution=.\ConsoleApp\ConsoleApp.sln" + if /i "%net_core%"=="true" set "solution=.\ConsoleApp\ConsoleApp.Core.sln" +) + +dotnet build %solution% +call :throw_on_native_failure +goto :eof + +:run_tests +set "method=%~1" +set "parallel_param=%~2" +set "build_from_source_param=%~3" + +if "%build_from_source_param%"=="" set "build_from_source_param=true" + +:: Array of filenames (simulated using variables) +set "filename0=weather" +set "filename1=bot.paths" +set "filename2=petstore" +set "filename3=petstore-expanded" +set "filename4=petstore-minimal" +set "filename5=petstore-simple" +set "filename6=petstore-with-external-docs" +set "filename7=api-with-examples" +set "filename8=callback-example" +set "filename9=link-example" +set "filename10=uber" +set "filename11=uspto" +set "filename12=hubspot-events" +set "filename13=hubspot-webhooks" +set "filename_count=14" + +if /i "%build_from_source_param%"=="true" ( + echo dotnet publish ..\src\Refitter\Refitter.csproj -p:PublishReadyToRun=true -o bin -f net8.0 + dotnet publish ..\src\Refitter\Refitter.csproj -p:PublishReadyToRun=true -o bin -f net8.0 + call :throw_on_native_failure + + echo refitter --version + .\bin\refitter --version + call :throw_on_native_failure +) + +:: Generate with settings files +call :generate_and_build " " " " "SwaggerPetstoreDirect.generated.cs" "--settings-file .\petstore.refitter" "" "" "%build_from_source_param%" +call :generate_and_build " " " " "" "--settings-file .\Apizr\petstore.apizr.refitter" "" ".\Apizr\Sample.csproj" "%build_from_source_param%" +call :generate_and_build " " " " "" "--settings-file .\MultipleFiles\petstore.refitter" "" "MultipleFiles\Client\Client.csproj" "%build_from_source_param%" + +:: Process versions and formats +for %%v in (v3.0 v2.0) do ( + for %%f in (json yaml) do ( + for /l %%i in (0,1,13) do ( + set "filename=!filename%%i!" + set "file_path=.\OpenAPI\%%v\!filename!.%%f" + + if exist "!file_path!" ( + copy "!file_path!" ".\openapi.%%f" >nul + + :: Create output path and namespace + set "output_path=!filename!.generated.cs" + set "output_path=!output_path:~0,1!!output_path:~1!" + call :capitalize output_path + + set "namespace=!filename!" + set "namespace=!namespace:-=!" + call :capitalize namespace + + :: Generate different variants + call :generate_and_build "%%f" "!namespace!.Disposable" "Disposable!output_path!" "--disposable" "true" "" "%build_from_source_param%" + call :generate_and_build "%%f" "!namespace!.MultipleFiles" "" "--multiple-files" "" "" "%build_from_source_param%" + call :generate_and_build "%%f" "!namespace!.SeparateContractsFile" "" "--contracts-output GeneratedCode/Contracts --contracts-namespace !namespace!.SeparateContractsFile.Contracts" "" "" "%build_from_source_param%" + call :generate_and_build "%%f" "!namespace!.Cancellation" "WithCancellation!output_path!" "--cancellation-tokens" "" "" "%build_from_source_param%" + call :generate_and_build "%%f" "!namespace!.Internal" "Internal!output_path!" "--internal" "" "" "%build_from_source_param%" + call :generate_and_build "%%f" "!namespace!.UsingApiResponse" "IApi!output_path!" "--use-api-response" "" "" "%build_from_source_param%" + call :generate_and_build "%%f" "!namespace!.UsingIObservable" "IObservable!output_path!" "--use-observable-response" "" "" "%build_from_source_param%" + call :generate_and_build "%%f" "!namespace!.UsingIsoDateFormat" "UsingIsoDateFormat!output_path!" "--use-iso-date-format" "" "" "%build_from_source_param%" + call :generate_and_build "%%f" "!namespace!.MultipleInterfaces" "MultipleInterfaces!output_path!" "--multiple-interfaces ByEndpoint" "" "" "%build_from_source_param%" + call :generate_and_build "%%f" "!namespace!.MultipleInterfaces" "MultipleInterfacesWithCustomName!output_path!" "--multiple-interfaces ByEndpoint --operation-name-template ExecuteAsync" "" "" "%build_from_source_param%" + call :generate_and_build "%%f" "!namespace!.TagFiltered" "TagFiltered!output_path!" "--tag pet --tag user --tag store" "" "" "%build_from_source_param%" + call :generate_and_build "%%f" "!namespace!.MatchPathFiltered" "MatchPathFiltered!output_path!" "--match-path ^^/pet/.*" "" "" "%build_from_source_param%" + call :generate_and_build "%%f" "!namespace!.ContractOnly" "ContractOnly!output_path!" "--contract-only" "" "" "%build_from_source_param%" + call :generate_and_build "%%f" "!namespace!.ImmutableRecords" "ImmutableRecords!output_path!" "--immutable-records" "true" "" "%build_from_source_param%" + call :generate_and_build "%%f" "!namespace!.PolymorphicSerialization" "PolymorphicSerialization!output_path!" "--use-polymorphic-serialization" "true" "" "%build_from_source_param%" + call :generate_and_build "%%f" "!namespace!.CollectionFormatCsv" "CollectionFormatCsv!output_path!" "--collection-format csv" "true" "" "%build_from_source_param%" + ) + ) + ) +) + +:: Process URLs +for %%u in ("https://petstore3.swagger.io/api/v3/openapi.json" "https://petstore3.swagger.io/api/v3/openapi.yaml") do ( + set "namespace=PetstoreFromUri" + set "output_path=PetstoreFromUri.generated.cs" + + del /q "*.generated.cs" 2>nul + + set "process_path=.\bin\refitter" + if /i "%build_from_source_param%"=="false" set "process_path=refitter" + + echo !process_path! %%u --namespace !namespace! --output .\GeneratedCode\!output_path! + !process_path! %%u --namespace !namespace! --output .\GeneratedCode\!output_path! + call :throw_on_native_failure + + echo. + echo Building ConsoleApp + echo. + dotnet build .\ConsoleApp\ConsoleApp.sln + call :throw_on_native_failure +) + +goto :eof + +:capitalize +set "temp_var=!%1!" +set "first_char=!temp_var:~0,1!" +set "rest_chars=!temp_var:~1!" +for %%a in (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) do ( + for %%b in (a b c d e f g h i j k l m n o p q r s t u v w x y z) do ( + if "!first_char!"=="%%b" set "first_char=%%a" + ) +) +set "%1=!first_char!!rest_chars!" +goto :eof + +:: Main execution +if /i "%USE_PRODUCTION%"=="true" ( + echo Running smoke tests in production mode + echo dotnet tool update -g refitter --prerelease + dotnet tool update -g refitter --prerelease + call :throw_on_native_failure + echo. +) + +echo Starting smoke tests... +set "start_time=%time%" +call :run_tests "dotnet-run" "%PARALLEL%" "!USE_PRODUCTION:true=false!" +set "end_time=%time%" +echo. +echo Smoke tests completed. + +endlocal