Skip to content

Commit 8e15e7d

Browse files
LEGLINK-951: Return 404 from DMRP endpoints when the module is disabled (#1817)
The Tenant build emits [assembly: ApplicationPart(DMRP)] for the DMRP project reference, so AddControllers() discovered the module's controllers even when DMRP:Enabled was false or missing. With no DMRP services registered, activating those controllers threw InvalidOperationException and every DMRP request returned 500. AddDmrpModule now strips the module's application part when the flag is off, so the routes are never mapped and requests fall through to 404. Claude-Session: https://claude.ai/code/session_01LVVXQjfgjXC4WwHtciJYEJ
1 parent 68b2842 commit 8e15e7d

2 files changed

Lines changed: 35 additions & 0 deletions

File tree

DotNet/DMRP/DependencyInjection/DmrpModuleExtensions.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,16 @@ public static bool AddDmrpModule<TDbContext>(this WebApplicationBuilder builder,
3535

3636
if (section.Get<DmrpSettings>()?.Enabled != true)
3737
{
38+
// The host's build emits [assembly: ApplicationPart("DMRP")] for the project reference,
39+
// so MVC discovers this module's controllers before AddDmrpModule runs. Left in place
40+
// without their services, those controllers turn every DMRP request into a 500; strip
41+
// the part so a disabled module has no routes at all.
42+
var moduleAssemblyName = typeof(DmrpModuleExtensions).Assembly.GetName().Name;
43+
foreach (var part in mvcBuilder.PartManager.ApplicationParts.Where(p => p.Name == moduleAssemblyName).ToList())
44+
{
45+
mvcBuilder.PartManager.ApplicationParts.Remove(part);
46+
}
47+
3848
return false;
3949
}
4050

DotNet/ServiceTests/UnitTests/DMRP/DmrpModuleExtensionsTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,5 +145,30 @@ public void AddDmrpModule_registers_nothing_when_disabled_or_unset(bool? enabled
145145
var dmrpAssembly = typeof(MeasureMapping).Assembly;
146146
Assert.DoesNotContain(mvcBuilder.PartManager.ApplicationParts, p => p.Name == dmrpAssembly.GetName().Name);
147147
}
148+
149+
[Theory]
150+
[InlineData(false)]
151+
[InlineData(null)]
152+
public void AddDmrpModule_removes_the_hosts_auto_discovered_part_when_disabled(bool? enabled)
153+
{
154+
var builder = CreateBuilder(enabled);
155+
var mvcBuilder = builder.Services.AddControllers();
156+
157+
// The Tenant build emits [assembly: ApplicationPart("DMRP")] for the project reference, so
158+
// in the real host the module's assembly is an application part before AddDmrpModule runs.
159+
// Recreate that here: the module must strip the part, or its controllers would be routable
160+
// without their services and every DMRP request would 500 instead of 404.
161+
var dmrpAssembly = typeof(MeasureMapping).Assembly;
162+
mvcBuilder.AddApplicationPart(dmrpAssembly);
163+
164+
var registered = builder.AddDmrpModule<TenantDbContext>(mvcBuilder);
165+
166+
Assert.False(registered);
167+
Assert.DoesNotContain(mvcBuilder.PartManager.ApplicationParts, p => p.Name == dmrpAssembly.GetName().Name);
168+
169+
var controllers = new ControllerFeature();
170+
mvcBuilder.PartManager.PopulateFeature(controllers);
171+
Assert.DoesNotContain(controllers.Controllers, c => c.Assembly == dmrpAssembly);
172+
}
148173
}
149174
}

0 commit comments

Comments
 (0)