Conversation
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>
waldekmastykarz
left a comment
There was a problem hiding this comment.
@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 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 |
There was a problem hiding this comment.
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 YAMLIConfigurationProviderfor loading root config from.yaml/.yml. - Updated config file discovery/loading to recognize
devproxyrc.yaml/.ymland route through a newAddConfigFilehelper. - 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. |
| // Skip nested merge keys in merged content | ||
| if (key == "<<") | ||
| { |
There was a problem hiding this comment.
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.
| // 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 |
| // 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); |
There was a problem hiding this comment.
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.
| Data[prefix] = scalarNode.Value; | ||
| break; | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
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.
| 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; | |
| } | |
| } |
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 supportDevProxy.Abstractions/Extensions/YamlConfigurationExtensions.cs- ASP.NET CoreIConfigurationProviderfor YAML filesModified Files
ConfigurationManagerExtensions.cs- Addeddevproxyrc.yaml/.ymlto config file discoveryBaseLoader.cs- Convert YAML plugin configs to JSON before processingConfigCommand.cs- Detect YAML config/mock files in downloaded samplesFeatures
.yamland.ymlextensions supported everywhere JSON is usedyes/no,on/off,true/false)Example
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/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)/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)/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
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.