Skip to content

Comments

Add support for YAML-based config#1491

Open
Copilot wants to merge 8 commits intomainfrom
copilot/add-yaml-config-support
Open

Add support for YAML-based config#1491
Copilot wants to merge 8 commits intomainfrom
copilot/add-yaml-config-support

Conversation

Copy link
Contributor

Copilot AI commented Jan 13, 2026

Adds YAML as an alternative to JSON for all configuration files. Supports YAML anchors and merge keys for reusable configuration blocks.

New Files

  • DevProxy.Abstractions/Utils/ProxyYaml.cs - YAML-to-JSON conversion with anchor/alias/merge key support
  • DevProxy.Abstractions/Extensions/YamlConfigurationExtensions.cs - ASP.NET Core IConfigurationProvider for YAML files

Modified Files

  • ConfigurationManagerExtensions.cs - Added devproxyrc.yaml/.yml to config file discovery
  • BaseLoader.cs - Convert YAML plugin configs to JSON before processing
  • ConfigCommand.cs - Detect YAML config/mock files in downloaded samples

Features

  • .yaml and .yml extensions supported everywhere JSON is used
  • YAML 1.1 boolean values (yes/no, on/off, true/false)
  • Full anchor/alias support with merge key precedence (explicit values override merged)

Example

definitions:
  throttled: &throttled
    statusCode: 429
    body: '{"error": "Too many requests"}'

mocks:
  - request:
      url: https://graph.microsoft.com/v1.0/users
    response:
      <<: *throttled
  - request:
      url: https://graph.microsoft.com/v1.0/groups
    response:
      <<: *throttled

Backward compatible—existing JSON configuration continues to work unchanged.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • aka.ms
    • Triggering command: /home/REDACTED/work/dev-proxy/dev-proxy/DevProxy/bin/Debug/net10.0/devproxy /home/REDACTED/work/dev-proxy/dev-proxy/DevProxy/bin/Debug/net10.0/devproxy --help (dns block)
    • Triggering command: /home/REDACTED/work/dev-proxy/dev-proxy/DevProxy/bin/Debug/net10.0/devproxy /home/REDACTED/work/dev-proxy/dev-proxy/DevProxy/bin/Debug/net10.0/devproxy --config-file devproxyrc.yaml (dns block)
    • Triggering command: /home/REDACTED/work/dev-proxy/dev-proxy/DevProxy/bin/Debug/net10.0/devproxy /home/REDACTED/work/dev-proxy/dev-proxy/DevProxy/bin/Debug/net10.0/devproxy --config-file devproxyrc-mocks.yaml (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>Add support for YAML-based config</issue_title>
<issue_description>Support both JSON- and YAML-based config.

YAML would be particularly beneficial for including comments (although they are supported in JSONC, but that's not always obvious) and multiline strings (ie. mocks).

One more thing that would be beneficial are anchors for repeated patterns:

definitions:
  throttled: &throttled
    statusCode: 429
    body: '{"error": "Too many requests"}'

responses:
  - url: https://graph.microsoft.com/v1.0/users
    <<: *throttled
  - url: https://graph.microsoft.com/v1.0/groups
    <<: *throttled

Notice how for both responses we reuse the same definition, significantly simplifying the config.

YAML config should be supported everywhere where we allow JSON-based config (root, plugins, etc)</issue_description>

Comments on the Issue (you are @copilot in this section)

@garrytrinder Nice idea! Let's do it 💪

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 4 commits January 13, 2026 13:25
Co-authored-by: waldekmastykarz <11164679+waldekmastykarz@users.noreply.github.com>
Co-authored-by: waldekmastykarz <11164679+waldekmastykarz@users.noreply.github.com>
Co-authored-by: waldekmastykarz <11164679+waldekmastykarz@users.noreply.github.com>
Co-authored-by: waldekmastykarz <11164679+waldekmastykarz@users.noreply.github.com>
Copilot AI changed the title [WIP] Add support for YAML-based configuration Add support for YAML-based config Jan 13, 2026
Copilot AI requested a review from waldekmastykarz January 13, 2026 13:40
Copy link
Collaborator

@waldekmastykarz waldekmastykarz left a comment

Choose a reason for hiding this comment

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

@copilot have you also considered supporting YAML in config files for plugins, like mocks, CRUD API, etc?

Copy link
Contributor Author

Copilot AI commented Jan 14, 2026

@copilot have you also considered supporting YAML in config files for plugins, like mocks, CRUD API, etc?

Yes, YAML is already supported for all plugin config files. The BaseLoader class (which all loaders like MockResponsesLoader, CrudApiDefinitionLoader, etc. inherit from) automatically detects .yaml/.yml files and converts them to JSON before processing.

Users can specify YAML files for mocks, CRUD API definitions, error responses, rate limiting configs, and all other plugin-specific configuration files - they just need to use the .yaml or .yml extension.

Copy link
Collaborator

@waldekmastykarz waldekmastykarz left a comment

Choose a reason for hiding this comment

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

@waldekmastykarz waldekmastykarz marked this pull request as ready for review February 22, 2026 10:22
@waldekmastykarz waldekmastykarz requested a review from a team as a code owner February 22, 2026 10:22
Copilot AI review requested due to automatic review settings February 22, 2026 10:22
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds first-class YAML support for Dev Proxy configuration files so users can author configs in either JSON/JSONC or YAML (including anchors and merge keys), while keeping existing JSON configs working.

Changes:

  • Added a YAML-to-JSON conversion utility (ProxyYaml) and a YAML IConfigurationProvider for loading root config from .yaml/.yml.
  • Updated config file discovery/loading to recognize devproxyrc.yaml/.yml and route through a new AddConfigFile helper.
  • Updated sample config/mock detection to include YAML files when scanning downloaded config folders.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
DevProxy/Extensions/ConfigurationManagerExtensions.cs Adds .yaml/.yml to devproxyrc discovery and uses AddConfigFile for JSON/YAML loading.
DevProxy/Commands/ConfigCommand.cs Expands sample config/mock detection to include YAML files.
DevProxy.Abstractions/Utils/ProxyYaml.cs Introduces YAML-to-JSON conversion with merge-key handling and scalar typing.
DevProxy.Abstractions/Plugins/BaseLoader.cs Converts YAML plugin/mock files to JSON before schema validation and parsing.
DevProxy.Abstractions/Extensions/YamlConfigurationExtensions.cs Adds YAML configuration source/provider and AddConfigFile extension method.

Comment on lines +158 to +160
// Skip nested merge keys in merged content
if (key == "<<")
{
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

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

CollectMergedValues skips nested YAML merge keys (<<) inside merged content. This means an anchored mapping that itself uses merges will not be fully expanded, which breaks expected YAML merge semantics and the PR’s stated merge-key support. Consider handling nested merge keys recursively (similar to FlattenMappingNode’s merge handling) rather than skipping them.

Suggested change
// Skip nested merge keys in merged content
if (key == "<<")
{
if (key == "<<")
{
// Handle nested merge keys recursively so that anchored mappings that
// themselves use merges are fully expanded.
YamlNode mergeNode = entry.Value;
if (mergeNode is YamlAliasNode aliasNode && aliasNode.Target is not null)
{
mergeNode = aliasNode.Target;
}
switch (mergeNode)
{
case YamlMappingNode mergeMapping:
// Merge the mapping into the current prefix
CollectMergedValues(mergeMapping, prefix, values);
break;
case YamlSequenceNode mergeSequence:
// Each mapping in the sequence is merged in order
foreach (var child in mergeSequence.Children)
{
YamlNode childNode = child;
if (childNode is YamlAliasNode childAlias && childAlias.Target is not null)
{
childNode = childAlias.Target;
}
if (childNode is YamlMappingNode childMapping)
{
CollectMergedValues(childMapping, prefix, values);
}
}
break;
}
// Skip normal processing for the merge key itself

Copilot uses AI. Check for mistakes.
Comment on lines +244 to +248
// YAML 1.1 and 1.2 null values
private static bool IsNullValue(string value) =>
value.Length == 0 ||
value.Equals("~", StringComparison.Ordinal) ||
value.Equals("null", StringComparison.OrdinalIgnoreCase);
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

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

IsNullValue treats any empty string (value.Length == 0) as null. This will convert an explicitly quoted empty string (e.g., "" or '') into JSON null, which changes configuration semantics. Consider distinguishing an empty plain scalar (null) from a quoted empty string (empty) using YamlScalarNode.Style/tag info, or avoid treating length==0 as null here.

Copilot uses AI. Check for mistakes.
Comment on lines +73 to +77
Data[prefix] = scalarNode.Value;
break;
}
}

Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

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

YamlConfigurationProvider currently stores scalar values verbatim (e.g., "yes"/"no"/"on"/"off"). ConfigurationBinder only parses booleans reliably from "true"/"false", so YAML 1.1 boolean values will not bind to bool properties like ValidateSchemas/AsSystemProxy as described in the PR. Normalize these YAML boolean tokens (and optionally null tokens like "~"/"null") to the canonical strings expected by the binder when flattening scalars.

Suggested change
Data[prefix] = scalarNode.Value;
break;
}
}
Data[prefix] = NormalizeScalar(scalarNode);
break;
}
}
private static string? NormalizeScalar(YamlScalarNode scalarNode)
{
var value = scalarNode.Value;
if (value is null)
{
return null;
}
// Only normalize plain (unquoted) scalars; quoted values should remain verbatim.
if (scalarNode.Style != YamlDotNet.Core.ScalarStyle.Plain)
{
return value;
}
var trimmed = value.Trim();
if (trimmed.Length == 0)
{
return value;
}
var lower = trimmed.ToLowerInvariant();
// YAML 1.1 boolean true values
switch (lower)
{
case "y":
case "yes":
case "true":
case "on":
return "true";
case "n":
case "no":
case "false":
case "off":
return "false";
case "~":
case "null":
return null;
default:
return value;
}
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for YAML-based config

2 participants