-
Notifications
You must be signed in to change notification settings - Fork 2
Description
📋 Problem Summary
The application currently has inconsistent representations of party/entity types across the codebase, leading to:
- Type confusion and potential bugs
- Mixing of Norwegian and English terminology
- Multiple string literals and enum types representing the same concept
- Complex conversion logic scattered throughout the frontend
Current Inconsistencies
1. Multiple Enum Definitions
// Frontend - Numeric enum (from BFF)
export enum PartyType {
Person = 1,
Organization = 2,
SelfIdentified = 3,
SubUnit = 4,
}
// Frontend - String enum with Norwegian! ❌
export enum ConnectionUserType {
Person = 'Person',
Organization = 'Organisasjon', // Norwegian mixed with English
Systemuser = 'Systembruker',
}// Backend - Multiple enums for same concept
public enum PartyType { Person = 1, Organization = 2, ... }
public enum AuthorizedPartyType { Person = 1, Organization = 2, ... }
public enum ResourcePartyType { PrivatePerson = 0, Company = 2, ... }2. Inconsistent String Representations
Throughout the codebase, we see:
'Person'(Pascal case)'person'(lowercase)'Organisasjon'(Norwegian)'Organization'(English)'organization'(lowercase English)'org'(abbreviated)'company'(UI component variant)'system'(for system users)
3. Multiple Conversion Points
Currently, type conversion happens in:
UserItem.tsx- MapsConnectionUserType→'person'/'company'/'system'UserPageHeader.tsx- MapsPartyType→'company'/'person'PageLayoutWrapper.tsx-getAccountType()helperPermissionBadge.tsx- Compares'organisasjon'(lowercase Norwegian!)useSelfConnection.ts- ConvertspartyTypeName.toString()- Various test files with mixed mock data
Affected Files (Sample)
/src/rtk/features/userInfoApi.ts- PartyType & ConnectionUserType enums/src/rtk/features/lookupApi.ts- Party interface withpartyTypeName/src/features/amUI/common/UserPageHeader/UserPageHeader.tsx/src/features/amUI/common/UserList/UserItem.tsx/src/features/amUI/common/AccessPackageList/PermissionBadge.tsx/src/features/amUI/users/NewUserModal/*- Uses'person'/'org'for tabs- Multiple test files with inconsistent mock data
🎯 Proposed Solution: BFF-Level Normalization
Strategy: Normalize at the Backend Boundary
Key Principle: All party type normalization should happen in the BFF layer, providing a single, consistent contract to the frontend.
Phase 1: Backend (BFF) Standardization
1.1 Create Canonical DTO Contract
// Altinn.AccessManagement.UI.Core/Models/Common/PartyTypeDto.cs
namespace Altinn.AccessManagement.UI.Core.Models.Common
{
/// <summary>
/// Standardized party type for all frontend responses.
/// This is the ONLY party type representation sent to frontend.
/// </summary>
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum PartyTypeDto
{
Person = 1,
Organization = 2,
SelfIdentified = 3,
SubUnit = 4
}
}1.2 Create Conversion Extensions
// Altinn.AccessManagement.UI.Core/Extensions/PartyTypeExtensions.cs
public static class PartyTypeExtensions
{
// Convert Register PartyType → PartyTypeDto
public static PartyTypeDto ToDto(this PartyType partyType) { ... }
// Convert AuthorizedPartyType → PartyTypeDto
public static PartyTypeDto ToDto(this AuthorizedPartyType authorizedType) { ... }
// Convert ResourcePartyType → PartyTypeDto
public static PartyTypeDto ToDto(this ResourcePartyType resourceType) { ... }
// Handle legacy string values (including Norwegian)
public static PartyTypeDto? FromString(string? partyTypeString)
{
var normalized = partyTypeString?.Trim().ToLowerInvariant();
return normalized switch
{
"person" => PartyTypeDto.Person,
"organization" => PartyTypeDto.Organization,
"organisasjon" => PartyTypeDto.Organization, // ✅ Handle Norwegian
"selfidentified" => PartyTypeDto.SelfIdentified,
"subunit" => PartyTypeDto.SubUnit,
_ => null
};
}
}1.3 Update BFF Response Models
public class PartyDto
{
public int PartyId { get; set; }
public string PartyUuid { get; set; }
public string Name { get; set; }
public PartyTypeDto PartyType { get; set; } // ✅ Standardized
// ... other fields
}
public class UserDto
{
public string Id { get; set; }
public string Name { get; set; }
public PartyTypeDto PartyType { get; set; } // ✅ Standardized
// ... other fields
}1.4 Apply Conversions in Controllers
All controllers returning party data should convert to PartyTypeDto:
LookupControllerUserControllerRightHolderController- Any other endpoints returning party/user data
Phase 2: Frontend Simplification
2.1 Update TypeScript Interfaces
// src/rtk/features/userInfoApi.ts
// ✅ Keep this enum (matches BFF exactly)
export enum PartyType {
Person = 1,
Organization = 2,
SelfIdentified = 3,
SubUnit = 4,
}
// ❌ REMOVE ConnectionUserType entirely
// No longer needed since BFF provides normalized data
export interface User {
id: string;
name: string;
partyType: PartyType; // ✅ Now comes normalized from BFF
variant?: string;
children: (User | ExtendedUser)[] | null;
keyValues: UserKeyValues | null;
}
export interface Party {
partyId: number;
partyUuid: string;
name: string;
partyType: PartyType; // ✅ Renamed from partyTypeName
// ... other fields
}2.2 Create Simple UI Utility
// src/utils/partyTypeUtils.ts
/**
* Convert PartyType to UI component icon type.
* This is the ONLY conversion needed in frontend.
*/
export const partyTypeToUIType = (
partyType: PartyType
): 'person' | 'company' | 'system' => {
switch (partyType) {
case PartyType.Person:
return 'person';
case PartyType.Organization:
case PartyType.SubUnit:
return 'company';
case PartyType.SelfIdentified:
return 'system';
default:
return 'person';
}
};
export const isPersonType = (partyType: PartyType): boolean =>
partyType === PartyType.Person;
export const isOrganizationType = (partyType: PartyType): boolean =>
partyType === PartyType.Organization || partyType === PartyType.SubUnit;2.3 Update Components
Replace all scattered conversion logic with the utility:
// Before ❌
type: user?.partyTypeName === PartyType.Organization ? 'company' : 'person'
// After ✅
type: partyTypeToUIType(user.partyType)📝 Implementation Checklist
Backend Tasks (BFF)
- Create
PartyTypeDtoenum inCore/Models/Common - Create
PartyTypeExtensionswith all conversion methods - Update
PartyDtomodel to usePartyTypefield - Update
UserDto/ConnectionDtomodels to usePartyTypefield - Update
LookupControllerto apply conversions - Update
UserControllerto apply conversions - Update
RightHolderControllerto apply conversions - Update any other controllers returning party data
- Add unit tests for conversion logic
- Update integration tests
- Deploy BFF changes
Frontend Tasks
- Create
src/utils/partyTypeUtils.tswith conversion utilities - Update
Partyinterface: renamepartyTypeName→partyType - Update
User/ExtendedUserinterfaces to usepartyType: PartyType - Remove
ConnectionUserTypeenum fromuserInfoApi.ts - Update
UserItem.tsxto usepartyTypeToUIType() - Update
UserPageHeader.tsxto usepartyTypeToUIType() - Update
PageLayoutWrapper.tsxto use utility functions - Update
PermissionBadge.tsxto usepartyTypeenum - Update
AccessPackageSection.tsxto usepartyTypeenum - Update
NewUserModalcomponents - Update
useSelfConnection.tsto usepartyTypedirectly - Remove all string comparisons (
'organisasjon','Organization', etc.) - Update all test files with correct mock data
- Remove deprecated code and helpers
Testing & Validation
- Test all party type conversions end-to-end
- Verify UI components render correct icons/types
- Test with Person, Organization, SubUnit, and SelfIdentified types
- Verify backwards compatibility during migration
- Test all affected pages (Users, Rights, Settings, etc.)
🎁 Benefits
✅ Single Source of Truth: All normalization happens in BFF
✅ Type Safety: Strong typing from backend to UI components
✅ Consistency: No more Norwegian/English mixing
✅ Maintainability: Centralized conversion logic
✅ Future-Proof: Easy to add new source systems or types
✅ Better DX: Clear API contract, better autocomplete
✅ Fewer Bugs: Eliminates string comparison errors
🚨 Migration Considerations
Backwards Compatibility Strategy
-
Phase 1: BFF changes with dual fields
- Add new
partyTypefield alongside existing fields - Keep deprecated fields for transition period
- Add new
-
Phase 2: Frontend migration
- Update to use new
partyTypefield - Remove references to old string types
- Update to use new
-
Phase 3: Cleanup
- Remove deprecated BFF fields
- Final testing and validation
Breaking Changes
- API response structure changes (mitigated by dual-field strategy)
- Frontend interface renames (
partyTypeName→partyType) - Removal of
ConnectionUserTypeenum
Risk Mitigation
- Deploy BFF changes first (backwards compatible)
- Frontend can migrate incrementally
- Comprehensive testing at each phase
- Feature flag for gradual rollout if needed
📚 Related Files to Review
Backend:
Altinn.AccessManagement.UI.Core/Models/Register/PartyType.csAltinn.AccessManagement.UI.Core/Enums/AuthorizedPartyType.csAltinn.AccessManagement.UI.Core/Enums/ResourcePartyType.csControllers/LookupController.csControllers/UserController.cs
Frontend:
src/rtk/features/userInfoApi.tssrc/rtk/features/lookupApi.tssrc/features/amUI/common/UserList/UserItem.tsxsrc/features/amUI/common/UserPageHeader/UserPageHeader.tsxsrc/features/amUI/common/PageLayoutWrapper/PageLayoutWrapper.tsx
💡 Questions to Address
- Should we introduce feature flags for gradual rollout?
- What's the timeline for backwards compatibility support?
- Do we need data migration for any stored party types?
- Should we add linting rules to prevent string literal party types?
Priority: High
Effort: Medium (2-3 sprints)
Type: Technical Debt / Refactoring
Labels: backend, frontend, api-contract, type-safety, refactoring
Metadata
Metadata
Assignees
Labels
Type
Projects
Status