Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"rollForward": false
},
"fantomas": {
"version": "7.0.0",
"version": "7.0.3",
"commands": [
"fantomas"
],
Expand Down
63 changes: 63 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
## Build, Test & Lint Commands

- **Build**: `dotnet fake build -t Build` (Release configuration)
- **Format Check**: `dotnet fake build -t CheckFormat` (validates Fantomas formatting)
- **Format**: `dotnet fake build -t Format` (applies Fantomas formatting)
- **All Tests**: `dotnet fake build -t RunTests` (builds + starts test server + runs all tests)
- **Unit Tests Only**: `dotnet build && dotnet tests/SwaggerProvider.Tests/bin/Release/net9.0/SwaggerProvider.Tests.dll`
- **Provider Tests (Integration)**:
1. Build test server: `dotnet build tests/Swashbuckle.WebApi.Server/Swashbuckle.WebApi.Server.fsproj -c Release`
2. Start server in background: `dotnet tests/Swashbuckle.WebApi.Server/bin/Release/net9.0/Swashbuckle.WebApi.Server.dll`
3. Build tests: `dotnet build SwaggerProvider.TestsAndDocs.sln -c Release`
4. Run tests: `dotnet tests/SwaggerProvider.ProviderTests/bin/Release/net9.0/SwaggerProvider.ProviderTests.dll`
- **Single Test**: Run via xunit runner: `dotnet [assembly] [filter]`

## Code Style Guidelines

**Language**: F# (net9.0 target framework)

**Imports & Namespaces**:

- `namespace [Module]` at file start; no `open` statements at module level
- Use `module [Name]` for nested modules
- Open dependencies after namespace declaration (e.g., `open Xunit`, `open FsUnitTyped`)
- Fully qualify internal modules: `SwaggerProvider.Internal.v2.Parser`, `SwaggerProvider.Internal.v3.Compilers`

**Formatting** (via Fantomas, EditorConfig enforced):

- 4-space indents, max 150 char line length
- `fsharp_max_function_binding_width=10`, `fsharp_max_infix_operator_expression=70`
- No space before parameter/lowercase invocation
- Multiline block brackets on same column, Stroustrup style enabled
- Bar before discriminated union declarations, max 3 blank lines

**Naming Conventions**:

- PascalCase for classes, types, modules, public members
- camelCase for local/private bindings, parameters
- Suffix test functions with `Tests` or use attributes like `[<Theory>]`, `[<Fact>]`

**Type Annotations**:

- Explicit return types for public functions (recommended)
- Use type inference for local bindings when obvious
- Generic type parameters: `'a`, `'b` (single quote prefix)

**Error Handling**:

- Use `Result<'T, 'Error>` or `Option<'T>` for fallible operations
- `failwith` or `failwithf` for errors in type providers and compilers
- Task-based async for I/O: `task { }` expressions in tests
- Match failures with `| _ -> ...` or pattern guards with `when`

**File Organization**:

- Tests use Xunit attributes: `[<Theory>]`, `[<Fact>]`, `[<MemberData>]`
- Design-time providers in `src/SwaggerProvider.DesignTime/`, runtime in `src/SwaggerProvider.Runtime/`
- Test schemas organized by OpenAPI version: `tests/.../Schemas/{v2,v3}/`

## Key Patterns

- Type Providers use `ProvidedApiClientBase` and compiler pipeline (DefinitionCompiler, OperationCompiler)
- SSRF protection enabled by default; disable with `SsrfProtection=false` static parameter
- Target net9.0; use implicit async/await (task expressions)
20 changes: 8 additions & 12 deletions src/SwaggerProvider.DesignTime/Provider.OpenApiClient.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ open Swagger
open SwaggerProvider.Internal
open SwaggerProvider.Internal.v3.Compilers

module Cache =
module OpenApiCache =
let providedTypes = Caching.createInMemoryCache(TimeSpan.FromSeconds 30.0)

/// The Open API Provider.
Expand Down Expand Up @@ -51,25 +51,21 @@ type public OpenApiClientTypeProvider(cfg: TypeProviderConfig) as this =
t.DefineStaticParameters(
staticParams,
fun typeName args ->
let schemaPath =
let schemaPathRaw = unbox<string> args.[0]
SchemaReader.getAbsolutePath cfg.ResolutionFolder schemaPathRaw

let schemaPathRaw = unbox<string> args.[0]
let ignoreOperationId = unbox<bool> args.[1]
let ignoreControllerPrefix = unbox<bool> args.[2]
let preferNullable = unbox<bool> args.[3]
let preferAsync = unbox<bool> args.[4]
let ssrfProtection = unbox<bool> args.[5]

let cacheKey =
(schemaPath, ignoreOperationId, ignoreControllerPrefix, preferNullable, preferAsync, ssrfProtection)
(schemaPathRaw, ignoreOperationId, ignoreControllerPrefix, preferNullable, preferAsync, ssrfProtection)
|> sprintf "%A"


let addCache() =
lazy
let schemaData =
SchemaReader.readSchemaPath (not ssrfProtection) "" schemaPath
SchemaReader.readSchemaPath (not ssrfProtection) "" cfg.ResolutionFolder schemaPathRaw
|> Async.RunSynchronously

let openApiReader = Microsoft.OpenApi.Readers.OpenApiStringReader()
Expand All @@ -96,18 +92,18 @@ type public OpenApiClientTypeProvider(cfg: TypeProviderConfig) as this =
let ty =
ProvidedTypeDefinition(tempAsm, ns, typeName, Some typeof<obj>, isErased = false, hideObjectMethods = true)

ty.AddXmlDoc("OpenAPI Provider for " + schemaPath)
ty.AddXmlDoc("OpenAPI Provider for " + schemaPathRaw)
ty.AddMembers tys
tempAsm.AddTypes [ ty ]

ty

try
Cache.providedTypes.GetOrAdd(cacheKey, addCache).Value
OpenApiCache.providedTypes.GetOrAdd(cacheKey, addCache).Value
with _ ->
Cache.providedTypes.Remove(cacheKey) |> ignore
OpenApiCache.providedTypes.Remove(cacheKey) |> ignore

Cache.providedTypes.GetOrAdd(cacheKey, addCache).Value
OpenApiCache.providedTypes.GetOrAdd(cacheKey, addCache).Value
)

t
Expand Down
16 changes: 8 additions & 8 deletions src/SwaggerProvider.DesignTime/Provider.SwaggerClient.fs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ open SwaggerProvider.Internal
open SwaggerProvider.Internal.v2.Parser
open SwaggerProvider.Internal.v2.Compilers

module SwaggerCache =
let providedTypes = Caching.createInMemoryCache(TimeSpan.FromSeconds 30.0)

/// The Swagger Type Provider.
[<TypeProvider; Obsolete("Use OpenApiClientTypeProvider when possible, it supports v2 & v3 schema formats.")>]
type public SwaggerTypeProvider(cfg: TypeProviderConfig) as this =
Expand Down Expand Up @@ -51,10 +54,7 @@ type public SwaggerTypeProvider(cfg: TypeProviderConfig) as this =
t.DefineStaticParameters(
staticParams,
fun typeName args ->
let schemaPath =
let schemaPathRaw = unbox<string> args.[0]
SchemaReader.getAbsolutePath cfg.ResolutionFolder schemaPathRaw

let schemaPathRaw = unbox<string> args.[0]
let headersStr = unbox<string> args.[1]
let ignoreOperationId = unbox<bool> args.[2]
let ignoreControllerPrefix = unbox<bool> args.[3]
Expand All @@ -63,13 +63,13 @@ type public SwaggerTypeProvider(cfg: TypeProviderConfig) as this =
let ssrfProtection = unbox<bool> args.[6]

let cacheKey =
(schemaPath, headersStr, ignoreOperationId, ignoreControllerPrefix, preferNullable, preferAsync, ssrfProtection)
(schemaPathRaw, headersStr, ignoreOperationId, ignoreControllerPrefix, preferNullable, preferAsync, ssrfProtection)
|> sprintf "%A"

let addCache() =
lazy
let schemaData =
SchemaReader.readSchemaPath (not ssrfProtection) headersStr schemaPath
SchemaReader.readSchemaPath (not ssrfProtection) headersStr cfg.ResolutionFolder schemaPathRaw
|> Async.RunSynchronously

let schema = SwaggerParser.parseSchema schemaData
Expand All @@ -87,13 +87,13 @@ type public SwaggerTypeProvider(cfg: TypeProviderConfig) as this =
let ty =
ProvidedTypeDefinition(tempAsm, ns, typeName, Some typeof<obj>, isErased = false, hideObjectMethods = true)

ty.AddXmlDoc("Swagger Provider for " + schemaPath)
ty.AddXmlDoc("Swagger Provider for " + schemaPathRaw)
ty.AddMembers tys
tempAsm.AddTypes [ ty ]

ty

Cache.providedTypes.GetOrAdd(cacheKey, addCache).Value
SwaggerCache.providedTypes.GetOrAdd(cacheKey, addCache).Value
)

t
Expand Down
Loading
Loading