Skip to content

Commit 7495e32

Browse files
hemanandrclaude
andcommitted
fix: resolve CIDR expansion not working properly
- Remove unreachable early return in ConfigurationParser.ParseAndValidateAsync() - Fix property mapping issues (http_path -> path, http_match -> expect_text) - Change ConfigurationParser service registration from Singleton to Scoped - Ensure CIDR ranges like 10.18.8.0/24 are properly expanded to individual IPs 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent e07f19b commit 7495e32

File tree

3 files changed

+37
-23
lines changed

3 files changed

+37
-23
lines changed

ThingConnect.Pulse.Server/Program.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,11 @@ public static async Task Main(string[] args)
135135

136136

137137
// Add configuration services
138-
builder.Services.AddSingleton<ConfigurationParser>(serviceProvider =>
138+
builder.Services.AddScoped<ConfigurationParser>(serviceProvider =>
139139
{
140140
ILogger<ConfigurationParser> logger = serviceProvider.GetRequiredService<ILogger<ConfigurationParser>>();
141-
return ConfigurationParser.CreateAsync(logger).GetAwaiter().GetResult();
141+
IDiscoveryService discoveryService = serviceProvider.GetRequiredService<IDiscoveryService>();
142+
return ConfigurationParser.CreateAsync(logger, discoveryService).GetAwaiter().GetResult();
142143
});
143144
builder.Services.AddScoped<IConfigurationService, ConfigurationService>();
144145
builder.Services.AddScoped<ISettingsService, SettingsService>();

ThingConnect.Pulse.Server/Services/ConfigurationParser.cs

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using NJsonSchema;
22
using ThingConnect.Pulse.Server.Data;
33
using ThingConnect.Pulse.Server.Models;
4+
using ThingConnect.Pulse.Server.Services.Monitoring;
45
using YamlDotNet.Serialization;
56
using YamlDotNet.Serialization.NamingConventions;
67

@@ -11,17 +12,19 @@ public sealed class ConfigurationParser
1112
private readonly IDeserializer _yamlDeserializer;
1213
private readonly JsonSchema _schema;
1314
private readonly ILogger<ConfigurationParser> _logger;
15+
private readonly IDiscoveryService _discoveryService;
1416

15-
private ConfigurationParser(JsonSchema schema, ILogger<ConfigurationParser> logger)
17+
private ConfigurationParser(JsonSchema schema, ILogger<ConfigurationParser> logger, IDiscoveryService discoveryService)
1618
{
1719
_yamlDeserializer = new DeserializerBuilder()
1820
.WithNamingConvention(UnderscoredNamingConvention.Instance)
1921
.Build();
2022
_schema = schema;
2123
_logger = logger;
24+
_discoveryService = discoveryService;
2225
}
2326

24-
public static async Task<ConfigurationParser> CreateAsync(ILogger<ConfigurationParser> logger)
27+
public static async Task<ConfigurationParser> CreateAsync(ILogger<ConfigurationParser> logger, IDiscoveryService discoveryService)
2528
{
2629
string assemblyLocation = System.Reflection.Assembly.GetExecutingAssembly().Location;
2730
string? assemblyDirectory = Path.GetDirectoryName(assemblyLocation);
@@ -43,7 +46,7 @@ public static async Task<ConfigurationParser> CreateAsync(ILogger<ConfigurationP
4346
string schemaJson = await File.ReadAllTextAsync(schemaPath);
4447
JsonSchema schema = await JsonSchema.FromJsonAsync(schemaJson);
4548
logger.LogInformation("Configuration schema loaded successfully");
46-
return new ConfigurationParser(schema, logger);
49+
return new ConfigurationParser(schema, logger, discoveryService);
4750
}
4851

4952
public Task<(ConfigurationYaml? Configuration, ValidationErrorsDto? Errors)> ParseAndValidateAsync(string yamlContent)
@@ -62,7 +65,6 @@ public static async Task<ConfigurationParser> CreateAsync(ILogger<ConfigurationP
6265
.Build();
6366
string configJson = serializer.Serialize(config);
6467

65-
return Task.FromResult<(ConfigurationYaml? Configuration, ValidationErrorsDto? Errors)>((config, null));
6668
// Perform schema validation
6769
ICollection<NJsonSchema.Validation.ValidationError> validationResults = _schema.Validate(configJson);
6870

@@ -135,7 +137,7 @@ public static async Task<ConfigurationParser> CreateAsync(ILogger<ConfigurationP
135137
}
136138
}
137139

138-
public (List<Group> Groups, List<Data.Endpoint> Endpoints) ConvertToEntities(ConfigurationYaml config)
140+
public async Task<(List<Group> Groups, List<Data.Endpoint> Endpoints)> ConvertToEntitiesAsync(ConfigurationYaml config)
139141
{
140142
var groups = config.Groups.Select(g => new Group
141143
{
@@ -146,21 +148,32 @@ public static async Task<ConfigurationParser> CreateAsync(ILogger<ConfigurationP
146148
SortOrder = g.SortOrder ?? 0
147149
}).ToList();
148150

149-
var endpoints = config.Targets.Select(t => new Data.Endpoint
151+
// Convert config targets to dynamic objects for DiscoveryService
152+
var dynamicTargets = config.Targets.Select(t => new
150153
{
151-
Id = Guid.NewGuid(),
152-
Name = t.Name ?? "Unnamed",
153-
GroupId = t.Group,
154-
Type = t.Type,
155-
Host = t.Host ?? t.Cidr ?? t.Wildcard ?? "localhost",
156-
Port = t.Port,
157-
IntervalSeconds = t.IntervalSeconds ?? config.Defaults.IntervalSeconds,
158-
TimeoutMs = t.TimeoutMs ?? config.Defaults.TimeoutMs,
159-
Retries = t.Retries ?? config.Defaults.Retries,
160-
HttpPath = t.HttpPath,
161-
HttpMatch = t.HttpMatch ?? config.Defaults.Http?.ExpectText,
162-
Enabled = t.Enabled ?? true
163-
}).ToList();
154+
type = t.Type.ToString().ToLower(),
155+
host = t.Host,
156+
cidr = t.Cidr,
157+
wildcard = t.Wildcard,
158+
port = t.Port,
159+
name = t.Name,
160+
group = t.Group,
161+
interval_seconds = t.IntervalSeconds ?? config.Defaults.IntervalSeconds,
162+
timeout_ms = t.TimeoutMs ?? config.Defaults.TimeoutMs,
163+
retries = t.Retries ?? config.Defaults.Retries,
164+
path = t.HttpPath,
165+
expect_text = t.HttpMatch ?? config.Defaults.Http?.ExpectText,
166+
enabled = t.Enabled ?? true,
167+
notes = t.Notes,
168+
expected_rtt_ms = t.ExpectedRttMs
169+
}).Cast<dynamic>().ToList();
170+
171+
// Use DiscoveryService to expand CIDR ranges, wildcards, and hostnames
172+
IEnumerable<Data.Endpoint> expandedEndpoints = await _discoveryService.ExpandTargetsAsync(dynamicTargets);
173+
var endpoints = expandedEndpoints.ToList();
174+
175+
_logger.LogInformation("Converted configuration with {GroupCount} groups and {TargetCount} targets into {EndpointCount} endpoints",
176+
groups.Count, config.Targets.Count, endpoints.Count);
164177

165178
return (groups, endpoints);
166179
}

ThingConnect.Pulse.Server/Services/ConfigurationService.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public async Task<ApplyResultDto> ApplyConfigurationAsync(string yamlContent, st
5858
};
5959
}
6060

61-
(List<Group> groups, List<Data.Endpoint> endpoints) = _parser.ConvertToEntities(configuration!);
61+
(List<Group> groups, List<Data.Endpoint> endpoints) = await _parser.ConvertToEntitiesAsync(configuration!);
6262

6363
using Microsoft.EntityFrameworkCore.Storage.IDbContextTransaction transaction = await _context.Database.BeginTransactionAsync();
6464
try
@@ -170,7 +170,7 @@ public async Task<ApplyResultDto> PreviewChangesAsync(string yamlContent)
170170
throw new InvalidOperationException("Configuration parsing returned null");
171171
}
172172

173-
(List<Group> groups, List<Data.Endpoint> endpoints) = _parser.ConvertToEntities(configuration!);
173+
(List<Group> groups, List<Data.Endpoint> endpoints) = await _parser.ConvertToEntitiesAsync(configuration!);
174174

175175
List<Group> existingGroups = await _context.Groups.ToListAsync();
176176
List<Data.Endpoint> existingEndpoints = await _context.Endpoints.ToListAsync();

0 commit comments

Comments
 (0)