Commit 7da9d94b authored by Eric Dieckman's avatar Eric Dieckman
Browse files

feat: add WiscNic person and group contact endpoints for subnets

Mirror the existing VLAN contact pattern on subnets: new wn_subnet_person_contacts
and wn_subnet_group_contacts junction tables, models, EF configs, and CRUD
endpoints on SubnetsController. Adds GET /people/{id}/subnets (federated direct
+ via group) and GET /wiscnic/groups/{id}/subnets reverse-lookup endpoints.
parent 51ae44da
Loading
Loading
Loading
Loading
+41 −0
Original line number Diff line number Diff line
@@ -120,4 +120,45 @@ public class PeopleController : ControllerBase

        return Ok(direct.Concat(viaGroup).ToList());
    }

    // GET: /people/1/subnets
    [ProducesResponseType<IList<PersonSubnetAssignment>>(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [HttpGet("{personId:long}/subnets", Name = "GetPersonSubnets")]
    public async Task<IActionResult> GetSubnetsAsync(long personId)
    {
        if (!await _context.People.AnyAsync(p => p.PersonId == personId))
            return NotFound();

        List<PersonSubnetAssignment> direct = await _context.WiscNicSubnetPersonContacts
            .Where(c => c.PersonId == personId)
            .Select(c => new PersonSubnetAssignment
            {
                SubnetId = c.SubnetId!.Value,
                Subnet = c.Subnet,
                Source = "direct",
                ContactTypeId = c.ContactTypeId,
                ContactType = c.ContactType,
            })
            .ToListAsync();

        List<PersonSubnetAssignment> viaGroup = await _context.WiscNicGroupMembers
            .Where(m => m.PersonId == personId)
            .Join(
                _context.WiscNicSubnetGroupContacts,
                m => m.GroupId,
                gc => gc.GroupId,
                (m, gc) => new { gc, m.Group })
            .Select(x => new PersonSubnetAssignment
            {
                SubnetId = x.gc.SubnetId!.Value,
                Subnet = x.gc.Subnet,
                Source = "group",
                GroupId = x.gc.GroupId,
                Group = x.Group,
            })
            .ToListAsync();

        return Ok(direct.Concat(viaGroup).ToList());
    }
}
+117 −0
Original line number Diff line number Diff line
@@ -81,4 +81,121 @@ public class SubnetsController : ControllerBase

        return Ok(entity);
    }

    // ######################################################
    // ### WISCNIC PERSON CONTACTS
    //

    // GET: /subnets/2/person-contacts
    [ProducesResponseType<IList<WiscNicSubnetPersonContact>>(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [HttpGet("{subnetId:long}/person-contacts", Name = "GetSubnetPersonContacts")]
    public async Task<IActionResult> GetPersonContactsAsync(long subnetId)
    {
        if (!await _context.Subnets.AnyAsync(s => s.SubnetId == subnetId))
            return NotFound();

        List<WiscNicSubnetPersonContact> result = await _context.WiscNicSubnetPersonContacts
            .Where(c => c.SubnetId == subnetId)
            .Include(c => c.Person)
            .Include(c => c.ContactType)
            .ToListAsync();

        return Ok(result);
    }

    // POST: /subnets/2/person-contacts
    [Authorize(Policy = Policies.CanEdit)]
    [ProducesResponseType<WiscNicSubnetPersonContact>(StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [HttpPost("{subnetId:long}/person-contacts", Name = "AddSubnetPersonContact")]
    public async Task<IActionResult> AddPersonContactAsync(long subnetId, [FromBody] WiscNicSubnetPersonContact data)
    {
        if (!await _context.Subnets.AnyAsync(s => s.SubnetId == subnetId))
            return NotFound();

        data.SubnetId = subnetId;
        _context.WiscNicSubnetPersonContacts.Add(data);
        await _context.SaveChangesAsync();

        return CreatedAtRoute("GetSubnetPersonContacts", new { subnetId }, data);
    }

    // DELETE: /subnets/2/person-contacts/5
    [Authorize(Policy = Policies.CanEdit)]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [HttpDelete("{subnetId:long}/person-contacts/{contactId:long}", Name = "RemoveSubnetPersonContact")]
    public async Task<IActionResult> RemovePersonContactAsync(long subnetId, long contactId)
    {
        WiscNicSubnetPersonContact? contact = await _context.WiscNicSubnetPersonContacts
            .Where(c => c.SubnetId == subnetId && c.WiscNicSubnetPersonContactId == contactId)
            .FirstOrDefaultAsync();

        if (contact == null)
            return NotFound();

        _context.WiscNicSubnetPersonContacts.Remove(contact);
        await _context.SaveChangesAsync();

        return NoContent();
    }

    // ######################################################
    // ### WISCNIC GROUP CONTACTS
    //

    // GET: /subnets/2/group-contacts
    [ProducesResponseType<IList<WiscNicSubnetGroupContact>>(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [HttpGet("{subnetId:long}/group-contacts", Name = "GetSubnetGroupContacts")]
    public async Task<IActionResult> GetGroupContactsAsync(long subnetId)
    {
        if (!await _context.Subnets.AnyAsync(s => s.SubnetId == subnetId))
            return NotFound();

        List<WiscNicSubnetGroupContact> result = await _context.WiscNicSubnetGroupContacts
            .Where(c => c.SubnetId == subnetId)
            .Include(c => c.Group)
            .ToListAsync();

        return Ok(result);
    }

    // POST: /subnets/2/group-contacts
    [Authorize(Policy = Policies.CanEdit)]
    [ProducesResponseType<WiscNicSubnetGroupContact>(StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [HttpPost("{subnetId:long}/group-contacts", Name = "AddSubnetGroupContact")]
    public async Task<IActionResult> AddGroupContactAsync(long subnetId, [FromBody] WiscNicSubnetGroupContact data)
    {
        if (!await _context.Subnets.AnyAsync(s => s.SubnetId == subnetId))
            return NotFound();

        data.SubnetId = subnetId;
        _context.WiscNicSubnetGroupContacts.Add(data);
        await _context.SaveChangesAsync();

        return CreatedAtRoute("GetSubnetGroupContacts", new { subnetId }, data);
    }

    // DELETE: /subnets/2/group-contacts/5
    [Authorize(Policy = Policies.CanEdit)]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [HttpDelete("{subnetId:long}/group-contacts/{contactId:long}", Name = "RemoveSubnetGroupContact")]
    public async Task<IActionResult> RemoveGroupContactAsync(long subnetId, long contactId)
    {
        WiscNicSubnetGroupContact? contact = await _context.WiscNicSubnetGroupContacts
            .Where(c => c.SubnetId == subnetId && c.WiscNicSubnetGroupContactId == contactId)
            .FirstOrDefaultAsync();

        if (contact == null)
            return NotFound();

        _context.WiscNicSubnetGroupContacts.Remove(contact);
        await _context.SaveChangesAsync();

        return NoContent();
    }
}
+16 −0
Original line number Diff line number Diff line
@@ -148,4 +148,20 @@ public class WiscNicGroupsController : ControllerBase

        return Ok(result);
    }

    // GET: /wiscnic/groups/1/subnets
    [ProducesResponseType<IList<WiscNicSubnetGroupContact>>(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [HttpGet("{groupId:long}/subnets", Name = "GetWiscNicGroupSubnets")]
    public async Task<IActionResult> GetSubnetsAsync(long groupId)
    {
        if (!await _context.WiscNicGroups.AnyAsync(g => g.WiscNicGroupId == groupId))
            return NotFound();

        List<WiscNicSubnetGroupContact> result = await _context.WiscNicSubnetGroupContacts
            .Where(c => c.GroupId == groupId)
            .ToListAsync();

        return Ok(result);
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -49,6 +49,8 @@ public class ApplicationDbContext : DbContext
    public DbSet<WiscNicContactType> WiscNicContactTypes => Set<WiscNicContactType>();
    public DbSet<WiscNicGroup> WiscNicGroups => Set<WiscNicGroup>();
    public DbSet<WiscNicGroupMember> WiscNicGroupMembers => Set<WiscNicGroupMember>();
    public DbSet<WiscNicSubnetGroupContact> WiscNicSubnetGroupContacts => Set<WiscNicSubnetGroupContact>();
    public DbSet<WiscNicSubnetPersonContact> WiscNicSubnetPersonContacts => Set<WiscNicSubnetPersonContact>();
    public DbSet<WiscNicVlanGroupContact> WiscNicVlanGroupContacts => Set<WiscNicVlanGroupContact>();
    public DbSet<WiscNicVlanPersonContact> WiscNicVlanPersonContacts => Set<WiscNicVlanPersonContact>();

+2 −0
Original line number Diff line number Diff line
@@ -48,6 +48,8 @@ internal sealed class ColumnNames
        internal const string ContactTypeId = "wn_contact_type_id";
        internal const string GroupId = "wn_group_id";
        internal const string GroupMemberId = "wn_group_member_id";
        internal const string SubnetGroupContactId = "wn_subnet_group_contact_id";
        internal const string SubnetPersonContactId = "wn_subnet_person_contact_id";
        internal const string VlanGroupContactId = "wn_vlan_group_contact_id";
        internal const string VlanPersonContactId = "wn_vlan_person_contact_id";
    }
Loading