결재 API 계약 보완 및 테스트 정리
This commit is contained in:
162
lib/features/auth/data/dtos/auth_session_dto.dart
Normal file
162
lib/features/auth/data/dtos/auth_session_dto.dart
Normal file
@@ -0,0 +1,162 @@
|
||||
import '../../../../core/common/utils/json_utils.dart';
|
||||
import '../../domain/entities/auth_permission.dart';
|
||||
import '../../domain/entities/auth_session.dart';
|
||||
import '../../domain/entities/authenticated_user.dart';
|
||||
|
||||
/// 로그인/토큰 갱신 응답을 역직렬화하는 DTO.
|
||||
class AuthSessionDto {
|
||||
AuthSessionDto({
|
||||
required this.accessToken,
|
||||
required this.refreshToken,
|
||||
required this.user,
|
||||
this.expiresAt,
|
||||
this.permissions = const [],
|
||||
});
|
||||
|
||||
final String accessToken;
|
||||
final String refreshToken;
|
||||
final DateTime? expiresAt;
|
||||
final AuthenticatedUser user;
|
||||
final List<AuthPermissionDto> permissions;
|
||||
|
||||
factory AuthSessionDto.fromJson(Map<String, dynamic> json) {
|
||||
final token = _readString(json, 'access_token');
|
||||
final refresh = _readString(json, 'refresh_token');
|
||||
final expires = _parseDate(_readString(json, 'expires_at'));
|
||||
final userMap = _readMap(json, 'user');
|
||||
final permissionList = _readList(json, 'permissions');
|
||||
return AuthSessionDto(
|
||||
accessToken: token ?? '',
|
||||
refreshToken: refresh ?? '',
|
||||
expiresAt: expires,
|
||||
user: _parseUser(userMap),
|
||||
permissions: permissionList
|
||||
.map(AuthPermissionDto.fromJson)
|
||||
.toList(growable: false),
|
||||
);
|
||||
}
|
||||
|
||||
AuthSession toEntity() {
|
||||
return AuthSession(
|
||||
accessToken: accessToken,
|
||||
refreshToken: refreshToken,
|
||||
expiresAt: expiresAt,
|
||||
user: user,
|
||||
permissions: permissions
|
||||
.map((dto) => dto.toEntity())
|
||||
.toList(growable: false),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AuthPermissionDto {
|
||||
const AuthPermissionDto({required this.resource, required this.actions});
|
||||
|
||||
final String resource;
|
||||
final List<String> actions;
|
||||
|
||||
factory AuthPermissionDto.fromJson(Map<String, dynamic>? json) {
|
||||
if (json == null) {
|
||||
throw const FormatException('권한 정보가 비어 있습니다.');
|
||||
}
|
||||
final resource = _readString(json, 'resource') ?? '';
|
||||
final actions = <String>[];
|
||||
final rawActions = json['actions'];
|
||||
if (rawActions is List) {
|
||||
for (final item in rawActions) {
|
||||
if (item is String) {
|
||||
final normalized = item.trim().toLowerCase();
|
||||
if (normalized.isNotEmpty) {
|
||||
actions.add(normalized);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (item is Map<String, dynamic>) {
|
||||
for (final entry in item.entries) {
|
||||
final normalized = entry.value.toString().trim().toLowerCase();
|
||||
if (normalized.isNotEmpty) {
|
||||
actions.add(normalized);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return AuthPermissionDto(
|
||||
resource: resource,
|
||||
actions: actions.toList(growable: false),
|
||||
);
|
||||
}
|
||||
|
||||
AuthPermission toEntity() =>
|
||||
AuthPermission(resource: resource, actions: actions);
|
||||
}
|
||||
|
||||
AuthenticatedUser _parseUser(Map<String, dynamic> json) {
|
||||
final id = _readOptionalInt(json, 'id') ?? 0;
|
||||
final name = _readString(json, 'name') ?? '';
|
||||
final employeeNo = _readString(json, 'employee_no');
|
||||
final email = _readString(json, 'email');
|
||||
final group = JsonUtils.extractMap(
|
||||
json,
|
||||
keys: const ['group', 'primary_group'],
|
||||
);
|
||||
return AuthenticatedUser(
|
||||
id: id,
|
||||
name: name,
|
||||
employeeNo: employeeNo,
|
||||
email: email,
|
||||
primaryGroupId: _readOptionalInt(group, 'id'),
|
||||
primaryGroupName: _readString(group, 'name'),
|
||||
);
|
||||
}
|
||||
|
||||
String? _readString(Map<String, dynamic>? source, String key) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
final value = source[key];
|
||||
if (value is String) {
|
||||
return value.trim();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Map<String, dynamic>> _readList(Map<String, dynamic> source, String key) {
|
||||
final value = source[key];
|
||||
if (value is List) {
|
||||
return value.whereType<Map<String, dynamic>>().toList(growable: false);
|
||||
}
|
||||
return const [];
|
||||
}
|
||||
|
||||
Map<String, dynamic> _readMap(Map<String, dynamic> source, String key) {
|
||||
final value = source[key];
|
||||
if (value is Map<String, dynamic>) {
|
||||
return value;
|
||||
}
|
||||
return const {};
|
||||
}
|
||||
|
||||
DateTime? _parseDate(String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return DateTime.tryParse(value);
|
||||
}
|
||||
|
||||
int? _readOptionalInt(Map<String, dynamic>? source, String key) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
final value = source[key];
|
||||
if (value is int) {
|
||||
return value;
|
||||
}
|
||||
if (value is String) {
|
||||
return int.tryParse(value);
|
||||
}
|
||||
if (value is double) {
|
||||
return value.round();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user