Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
6dee666
Add AGENTS.md
christianhelle Jun 10, 2026
c25a8c3
feat: add OpenApiOperationInfo record for partitioning strategy
christianhelle Jun 10, 2026
19c380b
feat: define IInterfacePartitioning seam for interface grouping strat…
christianhelle Jun 10, 2026
4e69267
feat: add SingleInterfacePartitioning adapter for single-interface mode
christianhelle Jun 10, 2026
571741f
feat: add ByEndpointInterfacePartitioning adapter for per-endpoint in…
christianhelle Jun 10, 2026
35c3d21
feat: add ByTagInterfacePartitioning adapter for per-tag interfaces
christianhelle Jun 10, 2026
ecdc3eb
feat: add InterfaceGenerator deep module with pluggable IInterfacePar…
christianhelle Jun 10, 2026
a474a16
refactor: update RefitGenerator to use InterfaceGenerator and IInterf…
christianhelle Jun 10, 2026
17b7995
refactor: remove old IRefitInterfaceGenerator and implementations (re…
christianhelle Jun 10, 2026
1bcdce6
fix: handle empty paths and skip deprecated-only groups in InterfaceG…
christianhelle Jun 10, 2026
7eb2e38
docs: add commit discipline section to AGENTS.md
christianhelle Jun 10, 2026
56492f5
refactor: standardize variable naming by removing underscores from Re…
christianhelle Jun 10, 2026
ea9f24b
Remove bot and hubspot example specs
christianhelle Jun 12, 2026
12dcb32
refactor: replace CreateInterfaceGenerator method with direct instant…
christianhelle Jun 12, 2026
f7421e5
refactor: simplify GroupBy usage in InterfaceGenerator
christianhelle Jun 12, 2026
bc2cbcc
refactor: sanitize interface name generation in partitioning classes
christianhelle Jun 12, 2026
6ee74d9
Move AGENTS.md to docs
christianhelle Jun 12, 2026
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
76 changes: 76 additions & 0 deletions docs/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# AGENTS.md — Refitter

OpenAPI-to-Refit code generator. Multi-package solution: CLI tool, MSBuild task, C# Source Generator, and Core library.

## Build & Test

- Solution: `src/Refitter.slnx`
- SDK: `10.0.300` with `rollForward: latestFeature` (see `global.json`)
- Build: `dotnet build -c Release src/Refitter.slnx`
- Test: `dotnet test --solution src/Refitter.slnx -c Release`

**Source Generator tests require a pre-build step.** Before running `dotnet test` on `Refitter.SourceGenerator.Tests`, the CI does:

```bash
rm -rf src/Refitter.SourceGenerator.Tests/AdditionalFiles/Generated
dotnet restore src/Refitter.SourceGenerator.Tests/Refitter.SourceGenerator.Tests.csproj
dotnet msbuild src/Refitter.SourceGenerator.Tests/Refitter.SourceGenerator.Tests.csproj
```

Run these first if source generator tests fail with missing generated types.

## Package Boundaries

| Project | TFM | Role |
|---|---|---|
| `Refitter.Core` | `netstandard2.0` | Core generation logic (NSwag-based) |
| `Refitter` | `net8.0;net9.0;net10.0` | CLI tool (`PackAsTool`, container support) |
| `Refitter.MSBuild` | `netstandard2.0` | MSBuild task; **requires Refitter CLI binaries** to be built first because it copies `../Refitter/bin/$(Configuration)/net{8,9,10}.0/**/*` into the package |
| `Refitter.SourceGenerator` | `netstandard2.0` | Roslyn source generator; emits code in-memory via `AddSource()` (not to disk since v2.0.0) |
| `Refitter.Tests` | `net10.0` | Unit tests |
| `Refitter.SourceGenerator.Tests` | `net8.0;net10.0` | Source generator integration tests |

## Testing

- **Framework**: TUnit (not xUnit). Attributes: `[Test]`, `[Arguments(...)]`.
- **Runner**: `Microsoft.Testing.Platform` (configured in `global.json`). Both test projects set `OutputType=Exe` and `GenerateProgramFile=false`.
- **Unit test pattern**: Scenario tests under `Refitter.Tests.Scenarios` contain an OpenAPI spec as a `const string`, generate code via `RefitGenerator.CreateAsync(...).Generate()`, assert string patterns, and verify compilation with `BuildHelper.BuildCSharp(generatedCode)`.
- **Temp file helper**: `SwaggerFileHelper.CreateSwaggerFile(contents)` creates a temp YAML file; `CreateSwaggerJsonFile` for JSON.
- **Build helper**: `BuildHelper.BuildCSharp()` writes generated code to a temp project and invokes `dotnet build` to verify it compiles. It defaults to `net8.0` but supports `net9.0` and `net10.0`.
- **Source generator tests**: Reference the generator as an `Analyzer` with `ReferenceOutputAssembly=false`. They verify generated types exist at runtime and have Refit HTTP attributes.

## CI / Smoke Tests

- Build workflow: `.github/workflows/build.yml`
- Smoke tests: `.github/workflows/smoke-tests.yml` runs `test/smoke-tests.ps1`
- The smoke test script publishes the CLI from source, generates clients against dozens of OpenAPI specs (v2.0, v3.0, v3.1, v3.4), and compiles them against console apps in `test/ConsoleApp/`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect smoke-tests.ps1 to verify claimed behavior

# Check if script exists and contains expected operations
test -f test/smoke-tests.ps1 && echo "✓ Smoke test script found" || echo "✗ Smoke test script NOT found"

# Look for key operations
echo "=== Checking for CLI publishing step ==="
grep -n "publish\|dotnet publish" test/smoke-tests.ps1 || echo "No publish command found"

echo "=== Checking for multi-spec generation ==="
grep -n "v2.0\|v3.0\|v3.1\|v3.4\|spec" test/smoke-tests.ps1 | head -10

echo "=== Checking for ConsoleApp reference ==="
grep -n "ConsoleApp" test/smoke-tests.ps1

Repository: christianhelle/refitter

Length of output: 2072


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="test/smoke-tests.ps1"
[ -f "$FILE" ] || { echo "Missing $FILE"; exit 1; }

echo "== Versions present =="
rg -n "v3\.1|v3\.4|v3\.0|v2\.0" "$FILE" || true

echo
echo "== ConsoleApp / path usage =="
rg -n "test/ConsoleApp|ConsoleApp" "$FILE" || true

echo
echo "== Generation loops / spec collection context =="
# Show the most relevant sections without dumping the whole file
# (covers the loop that collects generation tasks and likely where spec versions are enumerated)
start=$(rg -n "Collect generation tasks" "$FILE" | head -n1 | cut -d: -f1)
if [ -n "${start:-}" ]; then
  s=$((start-40)); e=$((start+140))
  sed -n "${s},${e}p" "$FILE" | cat -n
fi

echo
echo "== Client generation / compilation context =="
# Show around the BuildSolution -smokeTest calls and any surrounding generation/install steps
rg -n "smokeTest" "$FILE" | head -n 10 | sed -n '1,10p'
for line in $(rg -n "BuildSolution -solution \"\./ConsoleApp/.*\".*-smokeTest" "$FILE" | head -n 5 | cut -d: -f1); do
  s=$((line-30)); e=$((line+60))
  echo "--- around line $line ---"
  sed -n "${s},${e}p" "$FILE" | cat -n
done

echo
echo "== Count of generated spec files (heuristic) =="
# Try to estimate “dozens” by counting distinct spec paths that the script considers for generation.
# This looks for Test-Path checks and then counts unique SpecPath assignments referencing OpenAPI.
# (If the script uses globbing dynamically, this may undercount; still useful.)
rg -n "SpecPath = .*OpenAPI" "$FILE" || true
echo "Done."

Repository: christianhelle/refitter

Length of output: 33445


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="test/smoke-tests.ps1"

echo "== Context around dotnet publish =="
line_pub=$(rg -n "dotnet publish|Refitter\.csproj -c Release -o bin -f net10\.0" "$FILE" | head -n1 | cut -d: -f1 || true)
if [ -n "${line_pub}" ]; then
  s=$((line_pub-30)); e=$((line_pub+40))
  sed -n "${s},${e}p" "$FILE" | cat -n
else
  echo "No publish match found (unexpected)."
fi

echo
echo "== Find filename arrays for v2.0/v3.0/v3.1/v3.4 =="
rg -n "\\$filenames\\s*=|\\$v31Filenames\\s*=|\\$v34WebhookFilenames\\s*=" "$FILE" || true

echo
for var in "filenames" "v31Filenames" "v34WebhookFilenames"; do
  echo "== Context for \$$var =="
  match_line=$(rg -n "\\$${var}\\s*=" "$FILE" | head -n1 | cut -d: -f1 || true)
  if [ -z "${match_line}" ]; then
    echo "Not found: \$$var"
    continue
  fi
  s=$((match_line-10)); e=$((match_line+120))
  sed -n "${s},${e}p" "$FILE" | cat -n
  echo
done

echo "== Quick count heuristic (number of filenames elements) =="
# Extract simple list items from array literals like @("a","b",...) / @("a", "b")
python3 - <<'PY'
import re, pathlib
p=pathlib.Path("test/smoke-tests.ps1")
t=p.read_text(encoding="utf-8", errors="ignore")
def extract(var):
    # capture @(... ) after $var =
    m=re.search(rf"\${var}\s*=\s*@\(([\s\S]*?)\)\s*[\r\n]", t)
    if not m:
        return None
    block=m.group(1)
    items=re.findall(r'"([^"]+)"', block)
    return items
for var in ["filenames","v31Filenames","v34WebhookFilenames"]:
    items=extract(var)
    if items is None:
        print(var, "=> not an @(... ) literal / not easily parseable")
    else:
        print(var, "=>", len(items), "entries")
PY

Repository: christianhelle/refitter

Length of output: 5011


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="test/smoke-tests.ps1"

echo "== ConsoleApp path existence checks =="
ls -d test/ConsoleApp ./test/ConsoleApp 2>/dev/null || true
ls -d ConsoleApp ./ConsoleApp 2>/dev/null || true
ls -d test/ConsoleApp/ConsoleApp.slnx 2>/dev/null || true
ls -d test/ConsoleApp/ConsoleApp.Core.slnx 2>/dev/null || true
ls -d ConsoleApp/ConsoleApp.slnx 2>/dev/null || true
ls -d ConsoleApp/ConsoleApp.Core.slnx 2>/dev/null || true

echo
echo "== Locate filename arrays in test/smoke-tests.ps1 =="
rg -n '\$filenames\s*=|\\$v31Filenames\s*=|\\$v34WebhookFilenames\s*=' "$FILE" || true
rg -n '\$filenames\b|\$v31Filenames\b|\$v34WebhookFilenames\b' "$FILE" | head -n 50

echo
echo "== Parse array lengths from literals (heuristic) =="
python3 - <<'PY'
import re, pathlib
p=pathlib.Path("test/smoke-tests.ps1")
t=p.read_text(encoding="utf-8", errors="ignore")

def extract(var):
    # capture @(... ) after $var =
    m=re.search(rf"\${var}\s*=\s*@\(([\s\S]*?)\)\s*[\r\n]", t)
    if not m:
        return None
    block=m.group(1)
    # collect string literals inside the array
    items=re.findall(r'"([^"]+)"', block)
    return items

for var in ["filenames","v31Filenames","v34WebhookFilenames"]:
    items=extract(var)
    if items is None:
        print(f"{var}: not found as an @(... ) literal")
    else:
        print(f"{var}: {len(items)} entries")
        # show first/last few for sanity
        if len(items)>0:
            print("  first:", items[:5])
            print("  last :", items[-5:])
PY

Repository: christianhelle/refitter

Length of output: 947


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="test/smoke-tests.ps1"
echo "== File header =="
sed -n '1,120p' "$FILE" | cat -n

echo
echo "== Look for working-directory / script-root handling =="
rg -n "Set-Location|\$PSScriptRoot|\$PWD|Push-Location|WorkingDirectory|cd " "$FILE" || true

echo
echo "== Where RunTests is invoked with BuildFromSource =="
rg -n "RunTests\s*-" "$FILE" || true
sed -n '520,660p' "$FILE" | cat -n

Repository: christianhelle/refitter

Length of output: 9729


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find OpenAPI dir(s)
echo "== OpenAPI directories (by name) =="
fd -td d -a "OpenAPI" . | head -n 50 || true

# Prefer test/OpenAPI if present, else first match
OPENAPI_DIR="$(ls -d test/OpenAPI 2>/dev/null | head -n1 || true)"
if [ -z "${OPENAPI_DIR:-}" ]; then
  OPENAPI_DIR="$(fd -td d -a "OpenAPI" . | head -n1)"
fi
echo
echo "== Using OpenAPI dir =="
echo "$OPENAPI_DIR"

echo
echo "== Count existing spec files per version/format =="
for ver in v2.0 v3.0 v3.1 v3.4; do
  if [ -d "$OPENAPI_DIR/$ver" ]; then
    c_json=$(find "$OPENAPI_DIR/$ver" -type f -name "*.json" | wc -l)
    c_yaml=$(find "$OPENAPI_DIR/$ver" -type f -name "*.yaml" -o -name "*.yml" | wc -l)
    echo "$ver: json=$c_json yaml/yml=$c_yaml"
  else
    echo "$ver: missing"
  fi
done

echo
echo "== Total spec files for those versions (json/yaml) =="
total=0
for ver in v2.0 v3.0 v3.1 v3.4; do
  c=$(find "$OPENAPI_DIR/$ver" -type f \( -name "*.json" -o -name "*.yaml" -o -name "*.yml" \) 2>/dev/null | wc -l)
  total=$((total + c))
done
echo "total=$total"

Repository: christianhelle/refitter

Length of output: 1165


🏁 Script executed:

#!/bin/bash
set -euo pipefail

OPENAPI_DIR="test/OpenAPI"
echo "== test/OpenAPI tree (depth 3) =="
# show just the directory/file names without huge output
find "$OPENAPI_DIR" -maxdepth 3 -type d | sort | sed 's|^./||' || true
echo
echo "== test/OpenAPI version dirs =="
ls -1 "$OPENAPI_DIR" 2>/dev/null || true

echo
echo "== Check for v3.4 webhook-example files =="
ls -1 "$OPENAPI_DIR/v3.4" 2>/dev/null || true
# find the exact filename referenced by the script: webhook-example.<json|yaml>
find "$OPENAPI_DIR" -type f \( -name "webhook-example.json" -o -name "webhook-example.yaml" -o -name "webhook-example.yml" \) | sed 's|^./||' || true

echo
echo "== Count all spec files under test/OpenAPI =="
find "$OPENAPI_DIR" -type f \( -name "*.json" -o -name "*.yaml" -o -name "*.yml" \) | wc -l

Repository: christianhelle/refitter

Length of output: 388


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Find how smoke-tests.ps1 is invoked =="
rg -n "smoke-tests\.ps1" . || true

echo
echo "== Inspect RunTests definition + parameters =="
rg -n "function RunTests|RunTests\s*\{" test/smoke-tests.ps1 || true
# show around the first match (usually contains parameter list)
ln=$(rg -n "function RunTests|RunTests\s*\{" test/smoke-tests.ps1 | head -n1 | cut -d: -f1)
if [ -n "$ln" ]; then
  s=$((ln-40)); e=$((ln+120))
  sed -n "${s},${e}p" test/smoke-tests.ps1 | cat -n
fi

echo
echo "== Search for Set-Location / PSScriptRoot anywhere in file =="
rg -n "Set-Location|\\$PSScriptRoot|Push-Location|Pop-Location|cd " test/smoke-tests.ps1 || true

Repository: christianhelle/refitter

Length of output: 9924


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '1,120p' test/smoke-tests.sh | cat -n
rg -n "workdir|cd test|Set-Location|WorkingDirectory|smoke-tests\.ps1" .github/workflows/smoke-tests.yml .github/workflows -S || true
sed -n '1,160p' .github/workflows/smoke-tests.yml | cat -n || true

Repository: christianhelle/refitter

Length of output: 2973


Fix smoke-test behavior documented for v3.4 (and CLI publish is conditional).

  • test/smoke-tests.ps1 publishes src/Refitter/Refitter.csproj from source only when not running with -UseDocker and not in -UseProduction; in those modes it won’t do the source publish step.
  • It does compile generated code against ./ConsoleApp/... (CI runs the script with working-directory: test, so this resolves to test/ConsoleApp/), and it generates many v2.0/v3.0/v3.1 specs (there’s test/OpenAPI/v2.0, v3.0, v3.1), but test/OpenAPI/v3.4 doesn’t exist—so the v3.4 client generation loop will skip entirely.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@AGENTS.md` at line 46, Update the AGENTS.md smoke-test description to reflect
actual behavior: note that test/smoke-tests.ps1 only publishes
src/Refitter/Refitter.csproj from source when not run with -UseDocker and not in
-UseProduction, that generated clients are compiled against ./ConsoleApp/... (CI
runs with working-directory: test so that resolves to test/ConsoleApp/), and
that test/OpenAPI/v3.4 is not present so the v3.4 client generation loop is
skipped rather than producing clients; reference test/smoke-tests.ps1,
Refitter.csproj, -UseDocker, -UseProduction, ConsoleApp, and test/OpenAPI/v3.4
in the updated sentence.


## Code Style

- `.editorconfig` is authoritative.
- 4-space indentation; `crlf` line endings for C# files per `.editorconfig`.
- `var` is discouraged (`csharp_style_var_* = false:silent`).
- `TreatWarningsAsErrors` is enabled on most production projects.

## Key Conventions

- **New features** must have unit tests in the `Refitter.Tests.Scenarios` namespace following the pattern: spec string → generate → assert → build.
- **New CLI arguments** must be documented in `README.md` and in the `.refitter` file format docs.
- **`.refitter` files** are the settings format for all three distribution forms (CLI, MSBuild, Source Generator).
- **Source generator `.refitter` files** are automatically included as `AdditionalFiles` via the package props. The test project explicitly excludes `PropertyNamingPolicy.refitter` during design-time builds (`Condition="'$(DesignTimeBuild)' != 'true'"`).
- **MSBuild** package includes a custom `.targets` file that runs `RefitterGenerateTask` before `BeforeCompile`.

## Commit Discipline

- **Commit as often as possible** in small, logical groups. Each commit should represent one coherent change (e.g., one file created, one bug fix, one adapter added).
- **Build and run tests before every commit.** The minimum check is `dotnet build -c Release src/Refitter.slnx` followed by `dotnet test --solution src/Refitter.slnx -c Release --no-build`.
- **Never commit broken code.** If tests fail, fix them before committing. If the fix requires additional changes, commit those together as a "fix: ..." commit.
- **Commit messages** follow the pattern: `type: description` where type is one of `feat`, `fix`, `refactor`, `docs`, `test`, `chore`. Keep the subject line under 50 characters when possible.

## Documentation

- API docs are generated with DocFX from `docs/docfx_project/docfx.json`. To build locally:
```bash
dotnet tool update -g docfx
docfx docs/docfx_project/docfx.json
```
38 changes: 38 additions & 0 deletions src/Refitter.Core/ByEndpointInterfacePartitioning.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System.Text;
using NSwag;

namespace Refitter.Core;

internal class ByEndpointInterfacePartitioning(
RefitGeneratorSettings settings) : IInterfacePartitioning
{

public string GetGroupKey(OpenApiOperationInfo operation) => $"{operation.Path}#{operation.Verb}";

public string GetInterfaceName(string groupKey, string title, string baseOperationName) =>
$"I{baseOperationName.CapitalizeFirstCharacter()}".Sanitize();

public string GetInterfaceNameSuffix() => "Endpoint";

public string GetMethodName(OpenApiOperationInfo operation, string interfaceName, string baseOperationName)
{
var methodName = !string.IsNullOrWhiteSpace(settings.OperationNameTemplate)
? settings.OperationNameTemplate!.Replace("{operationName}", "Execute")
: "Execute";

return methodName;
}

public string GetDynamicQuerystringParameterType(string interfaceName, string methodName) =>
interfaceName.Substring(1).Replace("Endpoint", "QueryParams");
Comment on lines +26 to +27

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for uses of GetDynamicQuerystringParameterType and verify baseOperationName is never empty
rg -nP -C5 'GetDynamicQuerystringParameterType|GetBaseOperationName' --type=cs

Repository: christianhelle/refitter

Length of output: 8406


Guard against empty baseOperationName to avoid returning an empty dynamic query type
In src/Refitter.Core/ByEndpointInterfacePartitioning.cs (lines 26-27), GetDynamicQuerystringParameterType uses interfaceName.Substring(1) ...; if baseOperationName is empty, interfaceName can become "I" and this expression returns "" (no exception, but an empty generated type name). InterfaceGenerator explicitly sets baseOperationName to string.Empty when operations.Count == 0. Add a length/empty guard and fall back to a non-empty type name (e.g., methodName + "QueryParams" like the other partitionings).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Refitter.Core/ByEndpointInterfacePartitioning.cs` around lines 26 - 27,
GetDynamicQuerystringParameterType currently does
interfaceName.Substring(1).Replace("Endpoint","QueryParams") and can return an
empty name when baseOperationName is empty (InterfaceGenerator sets
baseOperationName = string.Empty for no operations). Add a guard in
GetDynamicQuerystringParameterType to detect an empty/too-short interfaceName
(e.g., interfaceName == "I" or interfaceName.Length <= 1) and in that case
return methodName + "QueryParams" as the fallback; otherwise keep the existing
substring+replace behavior so you never produce an empty dynamic query type.


public bool IsSingleInterface => false;

public void AppendInterfaceDocumentation(
OpenApiDocument document,
XmlDocumentationGenerator docGenerator,
string groupKey,
OpenApiOperationInfo representativeOperation,
StringBuilder code) =>
docGenerator.AppendInterfaceDocumentationByEndpoint(representativeOperation.Operation, code);
}
56 changes: 56 additions & 0 deletions src/Refitter.Core/ByTagInterfacePartitioning.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System.Text;
using NSwag;

namespace Refitter.Core;

internal class ByTagInterfacePartitioning : IInterfacePartitioning
{
private readonly RefitGeneratorSettings settings;
private readonly string ungroupedTitle;

public ByTagInterfacePartitioning(RefitGeneratorSettings settings, OpenApiDocument document)
{
this.settings = settings;
ungroupedTitle = settings.Naming.UseOpenApiTitle
? (document.Info?.Title ?? "ApiClient").Sanitize()
: settings.Naming.InterfaceName;
ungroupedTitle = ungroupedTitle.CapitalizeFirstCharacter();
}

public string GetGroupKey(OpenApiOperationInfo operation)
{
var tag = operation.Operation.Tags.FirstOrDefault();
return !string.IsNullOrWhiteSpace(tag)
? tag.SanitizeControllerTag()
: ungroupedTitle;
}

public string GetInterfaceName(string groupKey, string title, string baseOperationName) =>
$"I{groupKey.CapitalizeFirstCharacter()}".Sanitize();

public string GetInterfaceNameSuffix() => "Api";

public string GetMethodName(OpenApiOperationInfo operation, string interfaceName, string baseOperationName)
{
var methodName = baseOperationName.CapitalizeFirstCharacter();
if (settings.OperationNameTemplate?.Contains("{operationName}") ?? false)
{
methodName = settings.OperationNameTemplate!.Replace("{operationName}", methodName);
}

return methodName;
}

public string GetDynamicQuerystringParameterType(string interfaceName, string methodName) =>
methodName + "QueryParams";

public bool IsSingleInterface => false;

public void AppendInterfaceDocumentation(
OpenApiDocument document,
XmlDocumentationGenerator docGenerator,
string groupKey,
OpenApiOperationInfo representativeOperation,
StringBuilder code) =>
docGenerator.AppendInterfaceDocumentationByTag(document, groupKey, code);
}
47 changes: 47 additions & 0 deletions src/Refitter.Core/IInterfacePartitioning.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System.Text;
using NSwag;

namespace Refitter.Core;

internal interface IInterfacePartitioning
{
/// <summary>
/// Gets the group key for an operation. Operations with the same key are placed in the same interface.
/// </summary>
string GetGroupKey(OpenApiOperationInfo operation);

/// <summary>
/// Gets the raw interface name for a group key. The InterfaceGenerator handles deduplication.
/// </summary>
string GetInterfaceName(string groupKey, string title, string baseOperationName);

/// <summary>
/// Gets the raw method name for an operation. The InterfaceGenerator handles deduplication.
/// </summary>
string GetMethodName(OpenApiOperationInfo operation, string interfaceName, string baseOperationName);

/// <summary>
/// Gets the dynamic querystring parameter type name for a method.
/// </summary>
string GetDynamicQuerystringParameterType(string interfaceName, string methodName);

/// <summary>
/// Gets the suffix appended to the interface name for deduplication.
/// </summary>
string GetInterfaceNameSuffix();

/// <summary>
/// True if this partitioning generates exactly one interface.
/// </summary>
bool IsSingleInterface { get; }

/// <summary>
/// Appends interface-level documentation to the StringBuilder.
/// </summary>
void AppendInterfaceDocumentation(
OpenApiDocument document,
XmlDocumentationGenerator docGenerator,
string groupKey,
OpenApiOperationInfo representativeOperation,
StringBuilder code);
}
6 changes: 0 additions & 6 deletions src/Refitter.Core/IRefitInterfaceGenerator.cs

This file was deleted.

Loading
Loading