Add groups resource close #371 #378

Merged
simon merged 7 commits from feature/371-groups-resource into main 2026-01-27 17:17:26 +01:00
Owner

Description of the implemented changes

The changes were:

  • New Feature
  • Refactoring

What has been changed?

New Resources

  • Group Resource (lib/membership/group.ex)

    • Attributes: name, description, slug
    • Automatic slug generation from name (immutable after creation)
    • Case-insensitive uniqueness for name (LOWER(name) in DB)
    • Case-sensitive uniqueness for slug
    • Calculation: member_count (number of members in the group)
    • Relationships: has_many :member_groups, many_to_many :members
  • MemberGroup Resource (lib/membership/member_group.ex)

    • Join table for many-to-many relationship between Members and Groups
    • Unique constraint on (member_id, group_id) prevents duplicates
    • CASCADE delete for both foreign keys

Extended Resources

  • Member Resource (lib/membership/member.ex)

    • Added new relationships:
      • has_many :member_groups
      • many_to_many :groups
  • Membership Domain (lib/membership/membership.ex)

    • Added domain actions:
      • create_group/1, list_groups/0, update_group/2, destroy_group/1
      • create_member_group/1, list_member_groups/0, destroy_member_group/1

Refactoring

  • Generalized Slug Generation (lib/membership/changes/generate_slug.ex)
    • Created new generalized version: Mv.Membership.Changes.GenerateSlug
    • Used by both CustomField and Group resources (removed code duplication)
    • Removed old resource-specific versions:
      • lib/membership/custom_field/changes/generate_slug.ex
      • lib/membership/group/changes/generate_slug.ex

Database Migrations

  • Migration: 20260127141620_add_groups_and_member_groups.exs
    • groups table with UUID v7 primary key
    • Unique index on LOWER(name) for case-insensitive uniqueness
    • Unique index on slug for case-sensitive uniqueness
    • member_groups join table with CASCADE delete
    • Performance indexes on member_id and group_id

Tests

  • All 38 tests passing (5 test files):
    • test/membership/group_test.exs (27 tests)
    • test/membership/group_database_constraints_test.exs (3 tests)
    • test/membership/group_integration_test.exs (2 tests)
    • test/membership/member_group_test.exs (3 tests)
    • test/membership/member_groups_relationship_test.exs (3 tests)

Documentation

  • Updated docs/groups-architecture.md with correct reference to generalized slug generation

Definition of Done

Code Quality

  • No new technical depths
  • Linting passed (only warnings about unused imports that are actually needed)
  • Documentation is added where needed (all new modules have @moduledoc)

Accessibility

  • New elements are properly defined with html-tags
    • N/A: Backend-only feature, no UI changes
  • Colour contrast follows WCAG criteria
    • N/A: Backend-only feature
  • Aria labels are added when needed
    • N/A: Backend-only feature
  • Everything is accessible by keyboard
    • N/A: Backend-only feature
  • Tab-Order is comprehensible
    • N/A: Backend-only feature
  • All interactive elements have a visible focus
    • N/A: Backend-only feature

Testing

  • Tests for new code are written
    • 38 tests for Groups functionality
    • Tests for slug generation (CustomField tests still passing)
  • All tests pass
    • 38/38 tests passing for Groups
    • All CustomField tests still passing
  • axe-core dev tools show no critical or major issues
    • N/A: Backend-only feature, no UI changes

Additional Notes

Implementation Details

  • Slug Generation: Follows the same pattern as CustomFields, now in generalized form
  • Case-insensitive Uniqueness: Enforced both at application level (Ash validation) and database level (unique index on LOWER(name))
  • CASCADE Delete: Both foreign keys in member_groups have CASCADE delete, so associations are automatically removed when a member or group is deleted

Performance

  • Indexes on member_id and group_id for efficient queries
  • Unique index on (member_id, group_id) for fast duplicate checking
  • Preloading tests confirm that N+1 queries are avoided

Next Steps (not in this PR)

  • UI implementation for Groups management
  • Integration in Member Overview
  • Integration in Member Detail
  • Search Integration
  • Permissions

Breaking Changes

  • No breaking changes

Migration

  • Migration must be run: mix ash.setup or mix ecto.migrate
  • No data migrations needed (new tables only)
## Description of the implemented changes The changes were: - [x] New Feature - [x] Refactoring <!--- Implements Issue #371: Groups Resource & Database Schema. Adds group functionality to organize members into groups. Additionally, slug generation for CustomFields and Groups has been generalized. --> ## What has been changed? ### New Resources - **Group Resource** (`lib/membership/group.ex`) - Attributes: `name`, `description`, `slug` - Automatic slug generation from `name` (immutable after creation) - Case-insensitive uniqueness for `name` (LOWER(name) in DB) - Case-sensitive uniqueness for `slug` - Calculation: `member_count` (number of members in the group) - Relationships: `has_many :member_groups`, `many_to_many :members` - **MemberGroup Resource** (`lib/membership/member_group.ex`) - Join table for many-to-many relationship between Members and Groups - Unique constraint on `(member_id, group_id)` prevents duplicates - CASCADE delete for both foreign keys ### Extended Resources - **Member Resource** (`lib/membership/member.ex`) - Added new relationships: - `has_many :member_groups` - `many_to_many :groups` - **Membership Domain** (`lib/membership/membership.ex`) - Added domain actions: - `create_group/1`, `list_groups/0`, `update_group/2`, `destroy_group/1` - `create_member_group/1`, `list_member_groups/0`, `destroy_member_group/1` ### Refactoring - **Generalized Slug Generation** (`lib/membership/changes/generate_slug.ex`) - Created new generalized version: `Mv.Membership.Changes.GenerateSlug` - Used by both CustomField and Group resources (removed code duplication) - Removed old resource-specific versions: - `lib/membership/custom_field/changes/generate_slug.ex` - `lib/membership/group/changes/generate_slug.ex` ### Database Migrations - **Migration**: `20260127141620_add_groups_and_member_groups.exs` - `groups` table with UUID v7 primary key - Unique index on `LOWER(name)` for case-insensitive uniqueness - Unique index on `slug` for case-sensitive uniqueness - `member_groups` join table with CASCADE delete - Performance indexes on `member_id` and `group_id` ### Tests - All 38 tests passing (5 test files): - `test/membership/group_test.exs` (27 tests) - `test/membership/group_database_constraints_test.exs` (3 tests) - `test/membership/group_integration_test.exs` (2 tests) - `test/membership/member_group_test.exs` (3 tests) - `test/membership/member_groups_relationship_test.exs` (3 tests) ### Documentation - Updated `docs/groups-architecture.md` with correct reference to generalized slug generation ## Definition of Done ### Code Quality - [x] No new technical depths - [x] Linting passed (only warnings about unused imports that are actually needed) - [x] Documentation is added where needed (all new modules have @moduledoc) ### Accessibility - [ ] New elements are properly defined with html-tags - *N/A: Backend-only feature, no UI changes* - [ ] Colour contrast follows WCAG criteria - *N/A: Backend-only feature* - [ ] Aria labels are added when needed - *N/A: Backend-only feature* - [ ] Everything is accessible by keyboard - *N/A: Backend-only feature* - [ ] Tab-Order is comprehensible - *N/A: Backend-only feature* - [ ] All interactive elements have a visible focus - *N/A: Backend-only feature* ### Testing - [x] Tests for new code are written - 38 tests for Groups functionality - Tests for slug generation (CustomField tests still passing) - [x] All tests pass - 38/38 tests passing for Groups - All CustomField tests still passing - [ ] axe-core dev tools show no critical or major issues - *N/A: Backend-only feature, no UI changes* ## Additional Notes ### Implementation Details - **Slug Generation**: Follows the same pattern as CustomFields, now in generalized form - **Case-insensitive Uniqueness**: Enforced both at application level (Ash validation) and database level (unique index on LOWER(name)) - **CASCADE Delete**: Both foreign keys in `member_groups` have CASCADE delete, so associations are automatically removed when a member or group is deleted ### Performance - Indexes on `member_id` and `group_id` for efficient queries - Unique index on `(member_id, group_id)` for fast duplicate checking - Preloading tests confirm that N+1 queries are avoided ### Next Steps (not in this PR) - UI implementation for Groups management - Integration in Member Overview - Integration in Member Detail - Search Integration - Permissions ### Breaking Changes - No breaking changes ### Migration - Migration must be run: `mix ash.setup` or `mix ecto.migrate` - No data migrations needed (new tables only)
simon added 4 commits 2026-01-27 16:07:35 +01:00
docs: add slugs to group concept #371
All checks were successful
continuous-integration/drone/push Build is passing
2ebf289112
test: add tests for group resource #371
Some checks failed
continuous-integration/drone/push Build is failing
0216dfcbbb
docs: add testing philosophy to coding guideline
Some checks failed
continuous-integration/drone/push Build is failing
8e9fbe76cf
and update groups architecture docs #371
feat: add groups resource #371
Some checks failed
continuous-integration/drone/push Build is failing
6db64bf996
simon added 1 commit 2026-01-27 16:38:29 +01:00
test: resolve warnings
All checks were successful
continuous-integration/drone/push Build is passing
fc8306cfee
simon added 1 commit 2026-01-27 17:09:19 +01:00
refactor: fix review issues - member_count aggregate, migration down, docs, actor handling
All checks were successful
continuous-integration/drone/push Build is passing
e92c98b559
simon added 1 commit 2026-01-27 17:16:39 +01:00
Merge branch 'main' into feature/371-groups-resource
All checks were successful
continuous-integration/drone/push Build is passing
5df1da1573
simon changed title from WIP: Add groups resource close #371 to Add groups resource close #371 2026-01-27 17:16:47 +01:00
simon merged commit 0a2aa3bad0 into main 2026-01-27 17:17:26 +01:00
simon deleted branch feature/371-groups-resource 2026-01-27 17:17:27 +01:00
Sign in to join this conversation.
No description provided.