implemented user service (no auth!)
This commit is contained in:
parent
0ded723bb9
commit
8559b1c44e
@ -1,29 +0,0 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
|
||||||
|
|
||||||
part 'user_dto.g.dart';
|
|
||||||
|
|
||||||
@JsonSerializable()
|
|
||||||
class UserDto {
|
|
||||||
final String id;
|
|
||||||
final String name;
|
|
||||||
final String email;
|
|
||||||
|
|
||||||
// Optional: Füge zusätzliche Felder wie Timestamps hinzu
|
|
||||||
final DateTime? createdAt;
|
|
||||||
final DateTime? updatedAt;
|
|
||||||
|
|
||||||
UserDto({
|
|
||||||
required this.id,
|
|
||||||
required this.name,
|
|
||||||
required this.email,
|
|
||||||
this.createdAt,
|
|
||||||
this.updatedAt,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Factory-Methode zur Deserialisierung von JSON
|
|
||||||
factory UserDto.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$UserDtoFromJson(json);
|
|
||||||
|
|
||||||
/// Methode zur Serialisierung nach JSON
|
|
||||||
Map<String, dynamic> toJson() => _$UserDtoToJson(this);
|
|
||||||
}
|
|
9
backend-dart/lib/application/repository/provider.dart
Normal file
9
backend-dart/lib/application/repository/provider.dart
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import 'package:backend_dart/application/repository/user_repository_impl.dart';
|
||||||
|
import 'package:backend_dart/domain/repository/user_repository.dart';
|
||||||
|
import 'package:backend_dart/infrastructure/persistence/database_provider.dart';
|
||||||
|
import 'package:riverpod/riverpod.dart';
|
||||||
|
|
||||||
|
final userRepoProvider = Provider<UserRepository>((ref) {
|
||||||
|
final database = ref.read(databaseProvider);
|
||||||
|
return UserRepositoryImpl(database);
|
||||||
|
});
|
45
backend-dart/lib/application/repository/user_repository_impl.dart
Executable file
45
backend-dart/lib/application/repository/user_repository_impl.dart
Executable file
@ -0,0 +1,45 @@
|
|||||||
|
import 'package:backend_dart/domain/entities/user.dart';
|
||||||
|
import 'package:backend_dart/domain/interface/database.dart';
|
||||||
|
import 'package:backend_dart/domain/interface/error.dart';
|
||||||
|
import 'package:backend_dart/domain/repository/user_repository.dart';
|
||||||
|
import 'package:fpdart/fpdart.dart';
|
||||||
|
|
||||||
|
class UserRepositoryImpl implements UserRepository {
|
||||||
|
final IDatabase database;
|
||||||
|
UserRepositoryImpl(this.database);
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaskEither<IError, User> create(User user) {
|
||||||
|
return database.users
|
||||||
|
.generateId()
|
||||||
|
.map(
|
||||||
|
(id) => user.copyWith(id: id),
|
||||||
|
)
|
||||||
|
.flatMap(database.users.create);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaskEither<IError, User> findByEmail(String email) {
|
||||||
|
return database.users.findByEmail(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaskEither<IError, User> findById(String id) {
|
||||||
|
return database.users.findById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaskEither<IError, User> update(User user) {
|
||||||
|
return database.users.update(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaskEither<IError, void> delete(String id) {
|
||||||
|
return database.users.delete(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaskEither<IError, List<User>> findAll() {
|
||||||
|
return database.users.findAll();
|
||||||
|
}
|
||||||
|
}
|
20
backend-dart/lib/application/service/dto/user_dto.dart
Normal file
20
backend-dart/lib/application/service/dto/user_dto.dart
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
part 'user_dto.freezed.dart';
|
||||||
|
part 'user_dto.g.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class UserDto with _$UserDto {
|
||||||
|
const factory UserDto({
|
||||||
|
String? id,
|
||||||
|
String? name,
|
||||||
|
String? email,
|
||||||
|
String? password,
|
||||||
|
DateTime? createdAt,
|
||||||
|
DateTime? updatedAt,
|
||||||
|
}) = _UserDto;
|
||||||
|
|
||||||
|
/// JSON-Serialisierung
|
||||||
|
factory UserDto.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$UserDtoFromJson(json);
|
||||||
|
}
|
268
backend-dart/lib/application/service/dto/user_dto.freezed.dart
Normal file
268
backend-dart/lib/application/service/dto/user_dto.freezed.dart
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'user_dto.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||||
|
|
||||||
|
UserDto _$UserDtoFromJson(Map<String, dynamic> json) {
|
||||||
|
return _UserDto.fromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$UserDto {
|
||||||
|
String? get id => throw _privateConstructorUsedError;
|
||||||
|
String? get name => throw _privateConstructorUsedError;
|
||||||
|
String? get email => throw _privateConstructorUsedError;
|
||||||
|
String? get password => throw _privateConstructorUsedError;
|
||||||
|
DateTime? get createdAt => throw _privateConstructorUsedError;
|
||||||
|
DateTime? get updatedAt => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Serializes this UserDto to a JSON map.
|
||||||
|
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Create a copy of UserDto
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
$UserDtoCopyWith<UserDto> get copyWith => throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $UserDtoCopyWith<$Res> {
|
||||||
|
factory $UserDtoCopyWith(UserDto value, $Res Function(UserDto) then) =
|
||||||
|
_$UserDtoCopyWithImpl<$Res, UserDto>;
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{String? id,
|
||||||
|
String? name,
|
||||||
|
String? email,
|
||||||
|
String? password,
|
||||||
|
DateTime? createdAt,
|
||||||
|
DateTime? updatedAt});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$UserDtoCopyWithImpl<$Res, $Val extends UserDto>
|
||||||
|
implements $UserDtoCopyWith<$Res> {
|
||||||
|
_$UserDtoCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
/// Create a copy of UserDto
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? id = freezed,
|
||||||
|
Object? name = freezed,
|
||||||
|
Object? email = freezed,
|
||||||
|
Object? password = freezed,
|
||||||
|
Object? createdAt = freezed,
|
||||||
|
Object? updatedAt = freezed,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
id: freezed == id
|
||||||
|
? _value.id
|
||||||
|
: id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
name: freezed == name
|
||||||
|
? _value.name
|
||||||
|
: name // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
email: freezed == email
|
||||||
|
? _value.email
|
||||||
|
: email // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
password: freezed == password
|
||||||
|
? _value.password
|
||||||
|
: password // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
createdAt: freezed == createdAt
|
||||||
|
? _value.createdAt
|
||||||
|
: createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
updatedAt: freezed == updatedAt
|
||||||
|
? _value.updatedAt
|
||||||
|
: updatedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$UserDtoImplCopyWith<$Res> implements $UserDtoCopyWith<$Res> {
|
||||||
|
factory _$$UserDtoImplCopyWith(
|
||||||
|
_$UserDtoImpl value, $Res Function(_$UserDtoImpl) then) =
|
||||||
|
__$$UserDtoImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{String? id,
|
||||||
|
String? name,
|
||||||
|
String? email,
|
||||||
|
String? password,
|
||||||
|
DateTime? createdAt,
|
||||||
|
DateTime? updatedAt});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$UserDtoImplCopyWithImpl<$Res>
|
||||||
|
extends _$UserDtoCopyWithImpl<$Res, _$UserDtoImpl>
|
||||||
|
implements _$$UserDtoImplCopyWith<$Res> {
|
||||||
|
__$$UserDtoImplCopyWithImpl(
|
||||||
|
_$UserDtoImpl _value, $Res Function(_$UserDtoImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of UserDto
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? id = freezed,
|
||||||
|
Object? name = freezed,
|
||||||
|
Object? email = freezed,
|
||||||
|
Object? password = freezed,
|
||||||
|
Object? createdAt = freezed,
|
||||||
|
Object? updatedAt = freezed,
|
||||||
|
}) {
|
||||||
|
return _then(_$UserDtoImpl(
|
||||||
|
id: freezed == id
|
||||||
|
? _value.id
|
||||||
|
: id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
name: freezed == name
|
||||||
|
? _value.name
|
||||||
|
: name // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
email: freezed == email
|
||||||
|
? _value.email
|
||||||
|
: email // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
password: freezed == password
|
||||||
|
? _value.password
|
||||||
|
: password // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
createdAt: freezed == createdAt
|
||||||
|
? _value.createdAt
|
||||||
|
: createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
updatedAt: freezed == updatedAt
|
||||||
|
? _value.updatedAt
|
||||||
|
: updatedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
class _$UserDtoImpl implements _UserDto {
|
||||||
|
const _$UserDtoImpl(
|
||||||
|
{this.id,
|
||||||
|
this.name,
|
||||||
|
this.email,
|
||||||
|
this.password,
|
||||||
|
this.createdAt,
|
||||||
|
this.updatedAt});
|
||||||
|
|
||||||
|
factory _$UserDtoImpl.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$$UserDtoImplFromJson(json);
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String? id;
|
||||||
|
@override
|
||||||
|
final String? name;
|
||||||
|
@override
|
||||||
|
final String? email;
|
||||||
|
@override
|
||||||
|
final String? password;
|
||||||
|
@override
|
||||||
|
final DateTime? createdAt;
|
||||||
|
@override
|
||||||
|
final DateTime? updatedAt;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'UserDto(id: $id, name: $name, email: $email, password: $password, createdAt: $createdAt, updatedAt: $updatedAt)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$UserDtoImpl &&
|
||||||
|
(identical(other.id, id) || other.id == id) &&
|
||||||
|
(identical(other.name, name) || other.name == name) &&
|
||||||
|
(identical(other.email, email) || other.email == email) &&
|
||||||
|
(identical(other.password, password) ||
|
||||||
|
other.password == password) &&
|
||||||
|
(identical(other.createdAt, createdAt) ||
|
||||||
|
other.createdAt == createdAt) &&
|
||||||
|
(identical(other.updatedAt, updatedAt) ||
|
||||||
|
other.updatedAt == updatedAt));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
Object.hash(runtimeType, id, name, email, password, createdAt, updatedAt);
|
||||||
|
|
||||||
|
/// Create a copy of UserDto
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$UserDtoImplCopyWith<_$UserDtoImpl> get copyWith =>
|
||||||
|
__$$UserDtoImplCopyWithImpl<_$UserDtoImpl>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$$UserDtoImplToJson(
|
||||||
|
this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _UserDto implements UserDto {
|
||||||
|
const factory _UserDto(
|
||||||
|
{final String? id,
|
||||||
|
final String? name,
|
||||||
|
final String? email,
|
||||||
|
final String? password,
|
||||||
|
final DateTime? createdAt,
|
||||||
|
final DateTime? updatedAt}) = _$UserDtoImpl;
|
||||||
|
|
||||||
|
factory _UserDto.fromJson(Map<String, dynamic> json) = _$UserDtoImpl.fromJson;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get id;
|
||||||
|
@override
|
||||||
|
String? get name;
|
||||||
|
@override
|
||||||
|
String? get email;
|
||||||
|
@override
|
||||||
|
String? get password;
|
||||||
|
@override
|
||||||
|
DateTime? get createdAt;
|
||||||
|
@override
|
||||||
|
DateTime? get updatedAt;
|
||||||
|
|
||||||
|
/// Create a copy of UserDto
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$UserDtoImplCopyWith<_$UserDtoImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
@ -6,10 +6,12 @@ part of 'user_dto.dart';
|
|||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
UserDto _$UserDtoFromJson(Map<String, dynamic> json) => UserDto(
|
_$UserDtoImpl _$$UserDtoImplFromJson(Map<String, dynamic> json) =>
|
||||||
id: json['id'] as String,
|
_$UserDtoImpl(
|
||||||
name: json['name'] as String,
|
id: json['id'] as String?,
|
||||||
email: json['email'] as String,
|
name: json['name'] as String?,
|
||||||
|
email: json['email'] as String?,
|
||||||
|
password: json['password'] as String?,
|
||||||
createdAt: json['createdAt'] == null
|
createdAt: json['createdAt'] == null
|
||||||
? null
|
? null
|
||||||
: DateTime.parse(json['createdAt'] as String),
|
: DateTime.parse(json['createdAt'] as String),
|
||||||
@ -18,10 +20,12 @@ UserDto _$UserDtoFromJson(Map<String, dynamic> json) => UserDto(
|
|||||||
: DateTime.parse(json['updatedAt'] as String),
|
: DateTime.parse(json['updatedAt'] as String),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$UserDtoToJson(UserDto instance) => <String, dynamic>{
|
Map<String, dynamic> _$$UserDtoImplToJson(_$UserDtoImpl instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
'id': instance.id,
|
'id': instance.id,
|
||||||
'name': instance.name,
|
'name': instance.name,
|
||||||
'email': instance.email,
|
'email': instance.email,
|
||||||
|
'password': instance.password,
|
||||||
'createdAt': instance.createdAt?.toIso8601String(),
|
'createdAt': instance.createdAt?.toIso8601String(),
|
||||||
'updatedAt': instance.updatedAt?.toIso8601String(),
|
'updatedAt': instance.updatedAt?.toIso8601String(),
|
||||||
};
|
};
|
@ -0,0 +1,35 @@
|
|||||||
|
import 'package:backend_dart/application/service/dto/user_dto.dart';
|
||||||
|
import 'package:backend_dart/domain/entities/user.dart';
|
||||||
|
import 'package:backend_dart/domain/interface/error.dart';
|
||||||
|
import 'package:backend_dart/domain/interface/mapper.dart';
|
||||||
|
import 'package:fpdart/fpdart.dart';
|
||||||
|
|
||||||
|
class UserDtoMapper implements IMapper<User, UserDto> {
|
||||||
|
@override
|
||||||
|
TaskEither<IError, UserDto> to(User entity) => TaskEither.of(UserDto(
|
||||||
|
id: entity.id,
|
||||||
|
name: entity.name,
|
||||||
|
email: entity.email,
|
||||||
|
createdAt: entity.createdAt,
|
||||||
|
updatedAt: entity.updatedAt,
|
||||||
|
));
|
||||||
|
@override
|
||||||
|
TaskEither<IError, User> from(UserDto dto) => TaskEither.of(User(
|
||||||
|
id: dto.id,
|
||||||
|
name: dto.name,
|
||||||
|
email: dto.email,
|
||||||
|
password: dto.password,
|
||||||
|
createdAt: dto.createdAt,
|
||||||
|
updatedAt: dto.updatedAt,
|
||||||
|
));
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaskEither<IError, List<User>> listFrom(Iterable<UserDto> targets) {
|
||||||
|
return TaskEither.traverseList(targets.toList(), from);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaskEither<IError, List<UserDto>> listTo(Iterable<User> origins) {
|
||||||
|
return TaskEither.traverseList(origins.toList(), to);
|
||||||
|
}
|
||||||
|
}
|
88
backend-dart/lib/application/service/user_service.dart
Normal file
88
backend-dart/lib/application/service/user_service.dart
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:backend_dart/application/service/dto/user_dto.dart';
|
||||||
|
import 'package:backend_dart/application/service/mapper/user_dto_mapper.dart';
|
||||||
|
import 'package:backend_dart/common/response_helpers.dart';
|
||||||
|
import 'package:backend_dart/domain/errors/app_error.dart';
|
||||||
|
import 'package:backend_dart/domain/interface/error.dart';
|
||||||
|
import 'package:backend_dart/domain/repository/user_repository.dart';
|
||||||
|
import 'package:fpdart/fpdart.dart';
|
||||||
|
import 'package:shelf/shelf.dart';
|
||||||
|
import 'package:shelf_router/shelf_router.dart';
|
||||||
|
|
||||||
|
part 'user_service.g.dart'; // generated with 'pub run build_runner build'
|
||||||
|
|
||||||
|
class UserService {
|
||||||
|
final UserRepository users;
|
||||||
|
final UserDtoMapper mapper = UserDtoMapper();
|
||||||
|
UserService(this.users);
|
||||||
|
|
||||||
|
@Route.get('/')
|
||||||
|
Future<Response> listUsers(Request request) async {
|
||||||
|
return users
|
||||||
|
.findAll()
|
||||||
|
.flatMap(mapper.listTo)
|
||||||
|
.map((users) => users.map((user) => user.toJson()).toList())
|
||||||
|
.match((left) => ResponseHelpers.fromError(left),
|
||||||
|
(right) => Response.ok(jsonEncode(right)))
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Route.get('/<userId>')
|
||||||
|
Future<Response> fetchUser(Request request, String userId) async {
|
||||||
|
return users
|
||||||
|
.findById(userId)
|
||||||
|
.flatMap(mapper.to)
|
||||||
|
.match((left) => ResponseHelpers.fromError(left),
|
||||||
|
(right) => ResponseHelpers.jsonOk(right.toJson()))
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Route.post('/')
|
||||||
|
Future<Response> createUser(Request request) async {
|
||||||
|
return TaskEither<IError, UserDto>.tryCatch(() async {
|
||||||
|
final json = jsonDecode(await request.readAsString());
|
||||||
|
final user = UserDto.fromJson(json);
|
||||||
|
return user;
|
||||||
|
}, (error, stack) {
|
||||||
|
return AppError.inputError(
|
||||||
|
message: 'Failed to create user: ${error.toString()}');
|
||||||
|
})
|
||||||
|
.flatMap(mapper.from)
|
||||||
|
.flatMap(users.create)
|
||||||
|
.flatMap(mapper.to)
|
||||||
|
.match((left) => ResponseHelpers.fromError(left),
|
||||||
|
(right) => ResponseHelpers.jsonOk(right.toJson()))
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Route.put('/<userId>')
|
||||||
|
Future<Response> updateUser(Request request, String userId) async {
|
||||||
|
return TaskEither<IError, UserDto>.tryCatch(() async {
|
||||||
|
final json = jsonDecode(await request.readAsString());
|
||||||
|
final user = UserDto.fromJson(json);
|
||||||
|
return user;
|
||||||
|
}, (error, stack) {
|
||||||
|
return AppError.inputError(
|
||||||
|
message: 'Failed to update user: ${error.toString()}');
|
||||||
|
})
|
||||||
|
.flatMap(mapper.from)
|
||||||
|
.flatMap(users.update)
|
||||||
|
.flatMap(mapper.to)
|
||||||
|
.match((left) => ResponseHelpers.fromError(left),
|
||||||
|
(right) => ResponseHelpers.jsonOk(right.toJson()))
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Route.delete('/<userId>')
|
||||||
|
Future<Response> deleteUser(Request request, String userId) async {
|
||||||
|
return users
|
||||||
|
.delete(userId)
|
||||||
|
.match((left) => ResponseHelpers.fromError(left),
|
||||||
|
(right) => Response.ok('user deleted'))
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create router using the generate function defined in 'userservice.g.dart'.
|
||||||
|
Router get router => _$UserServiceRouter(this);
|
||||||
|
}
|
@ -18,5 +18,20 @@ Router _$UserServiceRouter(UserService service) {
|
|||||||
r'/<userId>',
|
r'/<userId>',
|
||||||
service.fetchUser,
|
service.fetchUser,
|
||||||
);
|
);
|
||||||
|
router.add(
|
||||||
|
'POST',
|
||||||
|
r'/',
|
||||||
|
service.createUser,
|
||||||
|
);
|
||||||
|
router.add(
|
||||||
|
'PUT',
|
||||||
|
r'/<userId>',
|
||||||
|
service.updateUser,
|
||||||
|
);
|
||||||
|
router.add(
|
||||||
|
'DELETE',
|
||||||
|
r'/<userId>',
|
||||||
|
service.deleteUser,
|
||||||
|
);
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
import 'package:backend_dart/application/repository/provider.dart';
|
||||||
|
import 'package:backend_dart/application/service/user_service.dart';
|
||||||
|
import 'package:riverpod/riverpod.dart';
|
||||||
|
|
||||||
|
final userServiceProvider = Provider<UserService>((ref) {
|
||||||
|
final database = ref.read(userRepoProvider);
|
||||||
|
return UserService(database);
|
||||||
|
});
|
@ -1,27 +0,0 @@
|
|||||||
import 'package:backend_dart/infrastructure/persistence/database.dart';
|
|
||||||
import 'package:shelf/shelf.dart';
|
|
||||||
import 'package:shelf_router/shelf_router.dart';
|
|
||||||
|
|
||||||
part 'user_service.g.dart'; // generated with 'pub run build_runner build'
|
|
||||||
|
|
||||||
class UserService {
|
|
||||||
final Database database;
|
|
||||||
UserService(this.database);
|
|
||||||
|
|
||||||
@Route.get('/')
|
|
||||||
Future<Response> listUsers(Request request) async {
|
|
||||||
return Response.ok('["user1"]');
|
|
||||||
}
|
|
||||||
|
|
||||||
@Route.get('/<userId>')
|
|
||||||
Future<Response> fetchUser(Request request, String userId) async {
|
|
||||||
final result = await database.users.findById(userId);
|
|
||||||
if (result != null) {
|
|
||||||
return Response.ok(result);
|
|
||||||
}
|
|
||||||
return Response.notFound('no such user');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create router using the generate function defined in 'userservice.g.dart'.
|
|
||||||
Router get router => _$UserServiceRouter(this);
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
import 'package:backend_dart/application/user_service/user_service.dart';
|
|
||||||
import 'package:backend_dart/infrastructure/persistence/database_provider.dart';
|
|
||||||
import 'package:riverpod/riverpod.dart';
|
|
||||||
|
|
||||||
final userServiceProvider = Provider<UserService>((ref) {
|
|
||||||
final database = ref.read(databaseProvider);
|
|
||||||
return UserService(database);
|
|
||||||
});
|
|
8
backend-dart/lib/common/error_on_null.dart
Normal file
8
backend-dart/lib/common/error_on_null.dart
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import 'package:backend_dart/domain/interface/error.dart';
|
||||||
|
import 'package:fpdart/fpdart.dart';
|
||||||
|
|
||||||
|
TaskEither<IError, T> Function(T?) errorOnNull<T>(IError error) {
|
||||||
|
return (T? value) {
|
||||||
|
return value != null ? TaskEither.of(value) : TaskEither.left(error);
|
||||||
|
};
|
||||||
|
}
|
84
backend-dart/lib/common/response_helpers.dart
Normal file
84
backend-dart/lib/common/response_helpers.dart
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'package:backend_dart/domain/errors/error_code.dart';
|
||||||
|
import 'package:backend_dart/domain/interface/error.dart';
|
||||||
|
import 'package:shelf/shelf.dart';
|
||||||
|
|
||||||
|
class ResponseHelpers {
|
||||||
|
/// Sendet eine JSON-Antwort mit einem 200-Statuscode
|
||||||
|
static Response jsonOk(Map<String, dynamic> data) {
|
||||||
|
return Response.ok(
|
||||||
|
jsonEncode(data),
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sendet eine JSON-Antwort mit einem spezifischen Statuscode
|
||||||
|
static Response jsonWithStatus(Map<String, dynamic> data, int statusCode) {
|
||||||
|
return Response(
|
||||||
|
statusCode,
|
||||||
|
body: jsonEncode(data),
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sendet eine JSON-Antwort für einen Fehler (z. B. 404 oder 500)
|
||||||
|
static Response jsonError(String message, {int statusCode = 500}) {
|
||||||
|
return Response(
|
||||||
|
statusCode,
|
||||||
|
body: jsonEncode({'error': message}),
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sendet eine einfache Textantwort
|
||||||
|
static Response textResponse(String message, {int statusCode = 200}) {
|
||||||
|
return Response(
|
||||||
|
statusCode,
|
||||||
|
body: message,
|
||||||
|
headers: {'Content-Type': 'text/plain'},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Response fromError(IError error) {
|
||||||
|
// Print error to console
|
||||||
|
print('Error: ${error.code.name} - ${error.message}');
|
||||||
|
//print(error.stackTrace);
|
||||||
|
return Response(
|
||||||
|
mapErrorCodeToHttpStatus(error.code),
|
||||||
|
body: jsonEncode({
|
||||||
|
'code': error.code.name,
|
||||||
|
'error': error.message,
|
||||||
|
'details': error.details,
|
||||||
|
}),
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int mapErrorCodeToHttpStatus(ErrorCode errorCode) {
|
||||||
|
switch (errorCode) {
|
||||||
|
case ErrorCode.networkError:
|
||||||
|
return 503; // Service Unavailable
|
||||||
|
case ErrorCode.validationError:
|
||||||
|
case ErrorCode.inputError:
|
||||||
|
return 400; // Bad Request
|
||||||
|
case ErrorCode.unexpectedError:
|
||||||
|
case ErrorCode.internalError:
|
||||||
|
case ErrorCode.databaseError:
|
||||||
|
return 500; // Internal Server Error
|
||||||
|
case ErrorCode.authenticationError:
|
||||||
|
return 401; // Unauthorized
|
||||||
|
case ErrorCode.permissionDenied:
|
||||||
|
return 403; // Forbidden
|
||||||
|
case ErrorCode.notFound:
|
||||||
|
return 404; // Not Found
|
||||||
|
case ErrorCode.mappingError:
|
||||||
|
case ErrorCode.outputError:
|
||||||
|
return 422; // Unprocessable Entity
|
||||||
|
case ErrorCode.exception:
|
||||||
|
case ErrorCode.unknownError:
|
||||||
|
return 520; // Unknown Error (Custom)
|
||||||
|
default:
|
||||||
|
return 500; // Fallback: Internal Server Error
|
||||||
|
}
|
||||||
|
}
|
18
backend-dart/lib/domain/data/user_data_source.dart
Normal file
18
backend-dart/lib/domain/data/user_data_source.dart
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import 'package:backend_dart/domain/interface/error.dart';
|
||||||
|
import 'package:fpdart/fpdart.dart';
|
||||||
|
|
||||||
|
abstract class UserDataSource<T> {
|
||||||
|
TaskEither<IError, T> create(T user);
|
||||||
|
|
||||||
|
TaskEither<IError, T> findByEmail(String email);
|
||||||
|
|
||||||
|
TaskEither<IError, T> findById(String id);
|
||||||
|
|
||||||
|
TaskEither<IError, T> update(T user);
|
||||||
|
|
||||||
|
TaskEither<IError, void> delete(String id);
|
||||||
|
|
||||||
|
TaskEither<IError, List<T>> findAll();
|
||||||
|
|
||||||
|
TaskEither<IError,String> generateId();
|
||||||
|
}
|
@ -1,14 +1,13 @@
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
part 'user.freezed.dart';
|
part 'user.freezed.dart';
|
||||||
part 'user.g.dart';
|
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
class User with _$User {
|
class User with _$User {
|
||||||
const factory User({
|
const factory User({
|
||||||
required String id,
|
String? id,
|
||||||
required String name,
|
String? name,
|
||||||
required String email,
|
String? email,
|
||||||
String? password,
|
String? password,
|
||||||
DateTime? createdAt,
|
DateTime? createdAt,
|
||||||
DateTime? updatedAt,
|
DateTime? updatedAt,
|
||||||
|
247
backend-dart/lib/domain/entities/user.freezed.dart
Normal file
247
backend-dart/lib/domain/entities/user.freezed.dart
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'user.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$User {
|
||||||
|
String? get id => throw _privateConstructorUsedError;
|
||||||
|
String? get name => throw _privateConstructorUsedError;
|
||||||
|
String? get email => throw _privateConstructorUsedError;
|
||||||
|
String? get password => throw _privateConstructorUsedError;
|
||||||
|
DateTime? get createdAt => throw _privateConstructorUsedError;
|
||||||
|
DateTime? get updatedAt => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Create a copy of User
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
$UserCopyWith<User> get copyWith => throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $UserCopyWith<$Res> {
|
||||||
|
factory $UserCopyWith(User value, $Res Function(User) then) =
|
||||||
|
_$UserCopyWithImpl<$Res, User>;
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{String? id,
|
||||||
|
String? name,
|
||||||
|
String? email,
|
||||||
|
String? password,
|
||||||
|
DateTime? createdAt,
|
||||||
|
DateTime? updatedAt});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$UserCopyWithImpl<$Res, $Val extends User>
|
||||||
|
implements $UserCopyWith<$Res> {
|
||||||
|
_$UserCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
/// Create a copy of User
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? id = freezed,
|
||||||
|
Object? name = freezed,
|
||||||
|
Object? email = freezed,
|
||||||
|
Object? password = freezed,
|
||||||
|
Object? createdAt = freezed,
|
||||||
|
Object? updatedAt = freezed,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
id: freezed == id
|
||||||
|
? _value.id
|
||||||
|
: id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
name: freezed == name
|
||||||
|
? _value.name
|
||||||
|
: name // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
email: freezed == email
|
||||||
|
? _value.email
|
||||||
|
: email // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
password: freezed == password
|
||||||
|
? _value.password
|
||||||
|
: password // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
createdAt: freezed == createdAt
|
||||||
|
? _value.createdAt
|
||||||
|
: createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
updatedAt: freezed == updatedAt
|
||||||
|
? _value.updatedAt
|
||||||
|
: updatedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$UserImplCopyWith<$Res> implements $UserCopyWith<$Res> {
|
||||||
|
factory _$$UserImplCopyWith(
|
||||||
|
_$UserImpl value, $Res Function(_$UserImpl) then) =
|
||||||
|
__$$UserImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{String? id,
|
||||||
|
String? name,
|
||||||
|
String? email,
|
||||||
|
String? password,
|
||||||
|
DateTime? createdAt,
|
||||||
|
DateTime? updatedAt});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$UserImplCopyWithImpl<$Res>
|
||||||
|
extends _$UserCopyWithImpl<$Res, _$UserImpl>
|
||||||
|
implements _$$UserImplCopyWith<$Res> {
|
||||||
|
__$$UserImplCopyWithImpl(_$UserImpl _value, $Res Function(_$UserImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of User
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? id = freezed,
|
||||||
|
Object? name = freezed,
|
||||||
|
Object? email = freezed,
|
||||||
|
Object? password = freezed,
|
||||||
|
Object? createdAt = freezed,
|
||||||
|
Object? updatedAt = freezed,
|
||||||
|
}) {
|
||||||
|
return _then(_$UserImpl(
|
||||||
|
id: freezed == id
|
||||||
|
? _value.id
|
||||||
|
: id // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
name: freezed == name
|
||||||
|
? _value.name
|
||||||
|
: name // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
email: freezed == email
|
||||||
|
? _value.email
|
||||||
|
: email // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
password: freezed == password
|
||||||
|
? _value.password
|
||||||
|
: password // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
|
createdAt: freezed == createdAt
|
||||||
|
? _value.createdAt
|
||||||
|
: createdAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
updatedAt: freezed == updatedAt
|
||||||
|
? _value.updatedAt
|
||||||
|
: updatedAt // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime?,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$UserImpl implements _User {
|
||||||
|
const _$UserImpl(
|
||||||
|
{this.id,
|
||||||
|
this.name,
|
||||||
|
this.email,
|
||||||
|
this.password,
|
||||||
|
this.createdAt,
|
||||||
|
this.updatedAt});
|
||||||
|
|
||||||
|
@override
|
||||||
|
final String? id;
|
||||||
|
@override
|
||||||
|
final String? name;
|
||||||
|
@override
|
||||||
|
final String? email;
|
||||||
|
@override
|
||||||
|
final String? password;
|
||||||
|
@override
|
||||||
|
final DateTime? createdAt;
|
||||||
|
@override
|
||||||
|
final DateTime? updatedAt;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'User(id: $id, name: $name, email: $email, password: $password, createdAt: $createdAt, updatedAt: $updatedAt)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$UserImpl &&
|
||||||
|
(identical(other.id, id) || other.id == id) &&
|
||||||
|
(identical(other.name, name) || other.name == name) &&
|
||||||
|
(identical(other.email, email) || other.email == email) &&
|
||||||
|
(identical(other.password, password) ||
|
||||||
|
other.password == password) &&
|
||||||
|
(identical(other.createdAt, createdAt) ||
|
||||||
|
other.createdAt == createdAt) &&
|
||||||
|
(identical(other.updatedAt, updatedAt) ||
|
||||||
|
other.updatedAt == updatedAt));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
Object.hash(runtimeType, id, name, email, password, createdAt, updatedAt);
|
||||||
|
|
||||||
|
/// Create a copy of User
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$UserImplCopyWith<_$UserImpl> get copyWith =>
|
||||||
|
__$$UserImplCopyWithImpl<_$UserImpl>(this, _$identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _User implements User {
|
||||||
|
const factory _User(
|
||||||
|
{final String? id,
|
||||||
|
final String? name,
|
||||||
|
final String? email,
|
||||||
|
final String? password,
|
||||||
|
final DateTime? createdAt,
|
||||||
|
final DateTime? updatedAt}) = _$UserImpl;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get id;
|
||||||
|
@override
|
||||||
|
String? get name;
|
||||||
|
@override
|
||||||
|
String? get email;
|
||||||
|
@override
|
||||||
|
String? get password;
|
||||||
|
@override
|
||||||
|
DateTime? get createdAt;
|
||||||
|
@override
|
||||||
|
DateTime? get updatedAt;
|
||||||
|
|
||||||
|
/// Create a copy of User
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$UserImplCopyWith<_$UserImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
51
backend-dart/lib/domain/errors/app_error.dart
Normal file
51
backend-dart/lib/domain/errors/app_error.dart
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import 'package:backend_dart/domain/interface/error.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'error_code.dart';
|
||||||
|
|
||||||
|
part 'app_error.freezed.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class AppError with _$AppError implements IError {
|
||||||
|
const factory AppError({
|
||||||
|
required ErrorCode code,
|
||||||
|
required String message,
|
||||||
|
Map<String, String>? details,
|
||||||
|
StackTrace? stackTrace,
|
||||||
|
Object? originalError,
|
||||||
|
}) = _AppError;
|
||||||
|
|
||||||
|
factory AppError.fromException(Exception e) {
|
||||||
|
return AppError(
|
||||||
|
code: ErrorCode.exception,
|
||||||
|
message: e.toString(),
|
||||||
|
originalError: e,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
factory AppError.fromErrorCode(ErrorCode code, {String? message}) {
|
||||||
|
return AppError(
|
||||||
|
code: code,
|
||||||
|
message: message ?? "No message",
|
||||||
|
stackTrace: StackTrace.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
factory AppError.notFound(String? message) {
|
||||||
|
return AppError.fromErrorCode(ErrorCode.notFound, message: message);
|
||||||
|
}
|
||||||
|
|
||||||
|
factory AppError.mappingError({String? message}) {
|
||||||
|
return AppError.fromErrorCode(ErrorCode.mappingError, message: message);
|
||||||
|
}
|
||||||
|
|
||||||
|
factory AppError.databaseError({String? message}) {
|
||||||
|
return AppError.fromErrorCode(ErrorCode.databaseError, message: message);
|
||||||
|
}
|
||||||
|
|
||||||
|
factory AppError.validationError({String? message}) {
|
||||||
|
return AppError.fromErrorCode(ErrorCode.validationError, message: message);
|
||||||
|
}
|
||||||
|
|
||||||
|
factory AppError.inputError({String? message}) {
|
||||||
|
return AppError.fromErrorCode(ErrorCode.inputError, message: message);
|
||||||
|
}
|
||||||
|
}
|
239
backend-dart/lib/domain/errors/app_error.freezed.dart
Normal file
239
backend-dart/lib/domain/errors/app_error.freezed.dart
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'app_error.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$AppError {
|
||||||
|
ErrorCode get code => throw _privateConstructorUsedError;
|
||||||
|
String get message => throw _privateConstructorUsedError;
|
||||||
|
Map<String, String>? get details => throw _privateConstructorUsedError;
|
||||||
|
StackTrace? get stackTrace => throw _privateConstructorUsedError;
|
||||||
|
Object? get originalError => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Create a copy of AppError
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
$AppErrorCopyWith<AppError> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $AppErrorCopyWith<$Res> {
|
||||||
|
factory $AppErrorCopyWith(AppError value, $Res Function(AppError) then) =
|
||||||
|
_$AppErrorCopyWithImpl<$Res, AppError>;
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{ErrorCode code,
|
||||||
|
String message,
|
||||||
|
Map<String, String>? details,
|
||||||
|
StackTrace? stackTrace,
|
||||||
|
Object? originalError});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$AppErrorCopyWithImpl<$Res, $Val extends AppError>
|
||||||
|
implements $AppErrorCopyWith<$Res> {
|
||||||
|
_$AppErrorCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
/// Create a copy of AppError
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? code = null,
|
||||||
|
Object? message = null,
|
||||||
|
Object? details = freezed,
|
||||||
|
Object? stackTrace = freezed,
|
||||||
|
Object? originalError = freezed,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
code: null == code
|
||||||
|
? _value.code
|
||||||
|
: code // ignore: cast_nullable_to_non_nullable
|
||||||
|
as ErrorCode,
|
||||||
|
message: null == message
|
||||||
|
? _value.message
|
||||||
|
: message // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
details: freezed == details
|
||||||
|
? _value.details
|
||||||
|
: details // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<String, String>?,
|
||||||
|
stackTrace: freezed == stackTrace
|
||||||
|
? _value.stackTrace
|
||||||
|
: stackTrace // ignore: cast_nullable_to_non_nullable
|
||||||
|
as StackTrace?,
|
||||||
|
originalError:
|
||||||
|
freezed == originalError ? _value.originalError : originalError,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$AppErrorImplCopyWith<$Res>
|
||||||
|
implements $AppErrorCopyWith<$Res> {
|
||||||
|
factory _$$AppErrorImplCopyWith(
|
||||||
|
_$AppErrorImpl value, $Res Function(_$AppErrorImpl) then) =
|
||||||
|
__$$AppErrorImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{ErrorCode code,
|
||||||
|
String message,
|
||||||
|
Map<String, String>? details,
|
||||||
|
StackTrace? stackTrace,
|
||||||
|
Object? originalError});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$AppErrorImplCopyWithImpl<$Res>
|
||||||
|
extends _$AppErrorCopyWithImpl<$Res, _$AppErrorImpl>
|
||||||
|
implements _$$AppErrorImplCopyWith<$Res> {
|
||||||
|
__$$AppErrorImplCopyWithImpl(
|
||||||
|
_$AppErrorImpl _value, $Res Function(_$AppErrorImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of AppError
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? code = null,
|
||||||
|
Object? message = null,
|
||||||
|
Object? details = freezed,
|
||||||
|
Object? stackTrace = freezed,
|
||||||
|
Object? originalError = freezed,
|
||||||
|
}) {
|
||||||
|
return _then(_$AppErrorImpl(
|
||||||
|
code: null == code
|
||||||
|
? _value.code
|
||||||
|
: code // ignore: cast_nullable_to_non_nullable
|
||||||
|
as ErrorCode,
|
||||||
|
message: null == message
|
||||||
|
? _value.message
|
||||||
|
: message // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
details: freezed == details
|
||||||
|
? _value._details
|
||||||
|
: details // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<String, String>?,
|
||||||
|
stackTrace: freezed == stackTrace
|
||||||
|
? _value.stackTrace
|
||||||
|
: stackTrace // ignore: cast_nullable_to_non_nullable
|
||||||
|
as StackTrace?,
|
||||||
|
originalError:
|
||||||
|
freezed == originalError ? _value.originalError : originalError,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$AppErrorImpl implements _AppError {
|
||||||
|
const _$AppErrorImpl(
|
||||||
|
{required this.code,
|
||||||
|
required this.message,
|
||||||
|
final Map<String, String>? details,
|
||||||
|
this.stackTrace,
|
||||||
|
this.originalError})
|
||||||
|
: _details = details;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final ErrorCode code;
|
||||||
|
@override
|
||||||
|
final String message;
|
||||||
|
final Map<String, String>? _details;
|
||||||
|
@override
|
||||||
|
Map<String, String>? get details {
|
||||||
|
final value = _details;
|
||||||
|
if (value == null) return null;
|
||||||
|
if (_details is EqualUnmodifiableMapView) return _details;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
final StackTrace? stackTrace;
|
||||||
|
@override
|
||||||
|
final Object? originalError;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'AppError(code: $code, message: $message, details: $details, stackTrace: $stackTrace, originalError: $originalError)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$AppErrorImpl &&
|
||||||
|
(identical(other.code, code) || other.code == code) &&
|
||||||
|
(identical(other.message, message) || other.message == message) &&
|
||||||
|
const DeepCollectionEquality().equals(other._details, _details) &&
|
||||||
|
(identical(other.stackTrace, stackTrace) ||
|
||||||
|
other.stackTrace == stackTrace) &&
|
||||||
|
const DeepCollectionEquality()
|
||||||
|
.equals(other.originalError, originalError));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(
|
||||||
|
runtimeType,
|
||||||
|
code,
|
||||||
|
message,
|
||||||
|
const DeepCollectionEquality().hash(_details),
|
||||||
|
stackTrace,
|
||||||
|
const DeepCollectionEquality().hash(originalError));
|
||||||
|
|
||||||
|
/// Create a copy of AppError
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$AppErrorImplCopyWith<_$AppErrorImpl> get copyWith =>
|
||||||
|
__$$AppErrorImplCopyWithImpl<_$AppErrorImpl>(this, _$identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _AppError implements AppError {
|
||||||
|
const factory _AppError(
|
||||||
|
{required final ErrorCode code,
|
||||||
|
required final String message,
|
||||||
|
final Map<String, String>? details,
|
||||||
|
final StackTrace? stackTrace,
|
||||||
|
final Object? originalError}) = _$AppErrorImpl;
|
||||||
|
|
||||||
|
@override
|
||||||
|
ErrorCode get code;
|
||||||
|
@override
|
||||||
|
String get message;
|
||||||
|
@override
|
||||||
|
Map<String, String>? get details;
|
||||||
|
@override
|
||||||
|
StackTrace? get stackTrace;
|
||||||
|
@override
|
||||||
|
Object? get originalError;
|
||||||
|
|
||||||
|
/// Create a copy of AppError
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$AppErrorImplCopyWith<_$AppErrorImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
15
backend-dart/lib/domain/errors/error_code.dart
Normal file
15
backend-dart/lib/domain/errors/error_code.dart
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
enum ErrorCode {
|
||||||
|
networkError, // Fehler bei der Netzwerkkommunikation
|
||||||
|
validationError, // Eingabevalidierungsfehler
|
||||||
|
unexpectedError, // Unerwarteter Fehler
|
||||||
|
authenticationError, // Authentifizierungsfehler
|
||||||
|
permissionDenied, // Berechtigungsfehler
|
||||||
|
notFound, // Ressource nicht gefunden
|
||||||
|
exception, // Ausnahme
|
||||||
|
mappingError, // Fehler beim Zuordnen von Daten
|
||||||
|
databaseError, // Datenbankfehler
|
||||||
|
inputError, // Eingabefehler
|
||||||
|
outputError, // Ausgabefehler
|
||||||
|
internalError, // Interner Fehler
|
||||||
|
unknownError, // Unbekannter Fehler
|
||||||
|
}
|
7
backend-dart/lib/domain/interface/database.dart
Normal file
7
backend-dart/lib/domain/interface/database.dart
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import 'package:backend_dart/domain/data/user_data_source.dart';
|
||||||
|
import 'package:backend_dart/domain/entities/user.dart';
|
||||||
|
|
||||||
|
abstract class IDatabase {
|
||||||
|
UserDataSource<User> get users;
|
||||||
|
Future<void> close();
|
||||||
|
}
|
8
backend-dart/lib/domain/interface/error.dart
Normal file
8
backend-dart/lib/domain/interface/error.dart
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import 'package:backend_dart/domain/errors/error_code.dart';
|
||||||
|
|
||||||
|
abstract class IError {
|
||||||
|
ErrorCode get code; // Fehlercode
|
||||||
|
String get message; // Fehlermeldung
|
||||||
|
StackTrace? get stackTrace; // Stacktrace
|
||||||
|
Map<String, String>? get details; // Zusätzliche Details
|
||||||
|
}
|
16
backend-dart/lib/domain/interface/mapper.dart
Normal file
16
backend-dart/lib/domain/interface/mapper.dart
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import 'package:backend_dart/domain/interface/error.dart';
|
||||||
|
import 'package:fpdart/fpdart.dart';
|
||||||
|
|
||||||
|
abstract class IMapper<U, V> {
|
||||||
|
/// Konvertiert von Typ U (Origin) zu Typ V (Target)
|
||||||
|
TaskEither<IError, V> to(U origin);
|
||||||
|
|
||||||
|
/// Konvertiert von Typ V (Target) zu Typ U (Origin)
|
||||||
|
TaskEither<IError, U> from(V target);
|
||||||
|
|
||||||
|
/// Konvertiert eine Liste von Typ U (Origin) zu einer Liste von Typ V (Target)
|
||||||
|
TaskEither<IError, List<V>> listTo(Iterable<U> origins);
|
||||||
|
|
||||||
|
/// Konvertiert eine Liste von Typ V (Target) zu einer Liste von Typ U (Origin)
|
||||||
|
TaskEither<IError, List<U>> listFrom(Iterable<V> targets);
|
||||||
|
}
|
@ -1,7 +0,0 @@
|
|||||||
import 'package:backend_dart/domain/entities/user.dart';
|
|
||||||
|
|
||||||
abstract class UserRepository {
|
|
||||||
Future<void> create(User user);
|
|
||||||
Future<User?> findByEmail(String email);
|
|
||||||
Future<User?> findById(String id);
|
|
||||||
}
|
|
17
backend-dart/lib/domain/repository/user_repository.dart
Executable file
17
backend-dart/lib/domain/repository/user_repository.dart
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
import 'package:backend_dart/domain/entities/user.dart';
|
||||||
|
import 'package:backend_dart/domain/interface/error.dart';
|
||||||
|
import 'package:fpdart/fpdart.dart';
|
||||||
|
|
||||||
|
abstract class UserRepository {
|
||||||
|
TaskEither<IError, User> create(User user);
|
||||||
|
|
||||||
|
TaskEither<IError, User> findByEmail(String email);
|
||||||
|
|
||||||
|
TaskEither<IError, User> findById(String id);
|
||||||
|
|
||||||
|
TaskEither<IError, User> update(User user);
|
||||||
|
|
||||||
|
TaskEither<IError, void> delete(String id);
|
||||||
|
|
||||||
|
TaskEither<IError, List<User>> findAll();
|
||||||
|
}
|
@ -1,13 +0,0 @@
|
|||||||
import 'package:backend_dart/infrastructure/persistence/db/client.dart';
|
|
||||||
import 'package:backend_dart/infrastructure/persistence/prisma_user_repository.dart';
|
|
||||||
|
|
||||||
class Database {
|
|
||||||
final prisma = PrismaClient();
|
|
||||||
late final PrismaUserRepository users;
|
|
||||||
Database() {
|
|
||||||
users = PrismaUserRepository(prisma);
|
|
||||||
print('Database initialized');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,8 +1,8 @@
|
|||||||
import 'package:backend_dart/infrastructure/persistence/database.dart';
|
import 'package:backend_dart/infrastructure/persistence/prisma_database.dart';
|
||||||
import 'package:riverpod/riverpod.dart';
|
import 'package:riverpod/riverpod.dart';
|
||||||
|
|
||||||
final databaseProvider = Provider<Database>((ref) {
|
final databaseProvider = Provider<PrismaDatabase>((ref) {
|
||||||
// Hier die Datenbankverbindung initialisieren
|
// Hier die Datenbankverbindung initialisieren
|
||||||
final database = Database();
|
final database = PrismaDatabase();
|
||||||
return database;
|
return database;
|
||||||
});
|
});
|
@ -0,0 +1,37 @@
|
|||||||
|
import 'package:backend_dart/domain/entities/user.dart';
|
||||||
|
import 'package:backend_dart/domain/interface/error.dart';
|
||||||
|
import 'package:backend_dart/domain/interface/mapper.dart';
|
||||||
|
import 'package:backend_dart/infrastructure/persistence/db/model.dart';
|
||||||
|
import 'package:fpdart/fpdart.dart';
|
||||||
|
|
||||||
|
class UserDboMapper implements IMapper<User, UserDbo> {
|
||||||
|
@override
|
||||||
|
TaskEither<IError, User> from(UserDbo target) => TaskEither.of(User(
|
||||||
|
id: target.id!,
|
||||||
|
name: target.name!,
|
||||||
|
email: target.email!,
|
||||||
|
password: target.password,
|
||||||
|
createdAt: target.createdAt,
|
||||||
|
updatedAt: target.updatedAt,
|
||||||
|
));
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaskEither<IError, List<User>> listFrom(Iterable<UserDbo> targets) {
|
||||||
|
return TaskEither.traverseList(targets.toList(), from);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaskEither<IError, List<UserDbo>> listTo(Iterable<User> origins) {
|
||||||
|
return TaskEither.traverseList(origins.toList(), to);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaskEither<IError, UserDbo> to(User origin) => TaskEither.of(UserDbo(
|
||||||
|
id: origin.id,
|
||||||
|
name: origin.name,
|
||||||
|
email: origin.email,
|
||||||
|
password: origin.password,
|
||||||
|
createdAt: origin.createdAt,
|
||||||
|
updatedAt: origin.updatedAt,
|
||||||
|
));
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
import 'package:backend_dart/domain/interface/database.dart';
|
||||||
|
import 'package:backend_dart/infrastructure/persistence/db/client.dart';
|
||||||
|
import 'package:backend_dart/infrastructure/persistence/prisma_user_data_source.dart';
|
||||||
|
|
||||||
|
class PrismaDatabase implements IDatabase {
|
||||||
|
final prisma = PrismaClient();
|
||||||
|
late final PrismaUserDataSource _users;
|
||||||
|
PrismaDatabase() {
|
||||||
|
_users = PrismaUserDataSource(prisma);
|
||||||
|
print('Database initialized');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
get users => _users;
|
||||||
|
@override
|
||||||
|
Future<void> close() {
|
||||||
|
return Future.value();
|
||||||
|
}
|
||||||
|
}
|
162
backend-dart/lib/infrastructure/persistence/prisma_user_data_source.dart
Executable file
162
backend-dart/lib/infrastructure/persistence/prisma_user_data_source.dart
Executable file
@ -0,0 +1,162 @@
|
|||||||
|
import 'package:backend_dart/common/error_on_null.dart';
|
||||||
|
import 'package:backend_dart/domain/data/user_data_source.dart';
|
||||||
|
import 'package:backend_dart/domain/entities/user.dart';
|
||||||
|
import 'package:backend_dart/domain/errors/app_error.dart';
|
||||||
|
import 'package:backend_dart/domain/interface/error.dart';
|
||||||
|
import 'package:backend_dart/infrastructure/persistence/db/model.dart';
|
||||||
|
import 'package:backend_dart/infrastructure/persistence/db/prisma.dart';
|
||||||
|
import 'package:backend_dart/infrastructure/persistence/mapper/user_dbo_mapper.dart';
|
||||||
|
import 'package:fpdart/fpdart.dart';
|
||||||
|
import 'package:orm/orm.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
|
import 'db/client.dart';
|
||||||
|
|
||||||
|
class PrismaUserDataSource implements UserDataSource<User> {
|
||||||
|
final PrismaClient prisma;
|
||||||
|
final UserDboMapper mapper = UserDboMapper();
|
||||||
|
PrismaUserDataSource(this.prisma);
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaskEither<IError, User> create(User user) {
|
||||||
|
return TaskEither<IError, UserDbo>.tryCatch(
|
||||||
|
() async {
|
||||||
|
if (user.password == null) {
|
||||||
|
throw Exception('Password is required');
|
||||||
|
}
|
||||||
|
if (user.email == null) {
|
||||||
|
throw Exception('Email is required');
|
||||||
|
}
|
||||||
|
final createdUser = await prisma.userDbo.create(
|
||||||
|
data: PrismaUnion.$1(UserDboCreateInput(
|
||||||
|
id: user.id,
|
||||||
|
name: user.name!,
|
||||||
|
email: user.email!,
|
||||||
|
password: user.password!,
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
return createdUser;
|
||||||
|
},
|
||||||
|
(error, _) => AppError.databaseError(
|
||||||
|
message: 'Failed to create user: ${error.toString()}',
|
||||||
|
),
|
||||||
|
).flatMap(mapper.from);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaskEither<IError, User> findByEmail(String email) {
|
||||||
|
return TaskEither<IError, UserDbo?>.tryCatch(
|
||||||
|
() async {
|
||||||
|
final user = await prisma.userDbo
|
||||||
|
.findUnique(where: UserDboWhereUniqueInput(email: email));
|
||||||
|
if (user == null) return null;
|
||||||
|
return user;
|
||||||
|
},
|
||||||
|
(error, _) => AppError.databaseError(
|
||||||
|
message: 'Failed to find user by email: ${error.toString()}',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.flatMap(errorOnNull(AppError.notFound(
|
||||||
|
'User with email $email found',
|
||||||
|
)))
|
||||||
|
.flatMap(mapper.from);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaskEither<IError, User> findById(String id) {
|
||||||
|
return TaskEither<IError, UserDbo?>.tryCatch(
|
||||||
|
() async {
|
||||||
|
final user = await prisma.userDbo
|
||||||
|
.findUnique(where: UserDboWhereUniqueInput(id: id));
|
||||||
|
return user;
|
||||||
|
},
|
||||||
|
(error, _) => AppError.databaseError(
|
||||||
|
message: 'Failed to find user by ID: ${error.toString()}',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.flatMap(errorOnNull(AppError.notFound(
|
||||||
|
"User with id $id not found",
|
||||||
|
)))
|
||||||
|
.flatMap(mapper.from);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaskEither<IError, User> update(User user) {
|
||||||
|
return mapper
|
||||||
|
.to(user)
|
||||||
|
.flatMap((userDbo) => TaskEither.tryCatch(() async {
|
||||||
|
// Führe das Update durch
|
||||||
|
if (userDbo.id == null) {
|
||||||
|
throw Exception('User ID is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
final updatedUser = await prisma.userDbo.update(
|
||||||
|
data: PrismaUnion.$2(
|
||||||
|
UserDboUncheckedUpdateInput(
|
||||||
|
id: PrismaUnion.$1(userDbo.id!),
|
||||||
|
name: userDbo.name != null
|
||||||
|
? PrismaUnion.$1(userDbo.name!)
|
||||||
|
: null,
|
||||||
|
email: userDbo.email != null
|
||||||
|
? PrismaUnion.$1(userDbo.email!)
|
||||||
|
: null,
|
||||||
|
password: userDbo.password != null
|
||||||
|
? PrismaUnion.$1(userDbo.password!)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
where: UserDboWhereUniqueInput(id: userDbo.id),
|
||||||
|
);
|
||||||
|
|
||||||
|
return updatedUser;
|
||||||
|
},
|
||||||
|
(error, _) => AppError.databaseError(
|
||||||
|
message: 'Failed to update user: ${error.toString()}',
|
||||||
|
)))
|
||||||
|
.flatMap(errorOnNull(AppError.notFound(
|
||||||
|
'User not found',
|
||||||
|
)))
|
||||||
|
.flatMap(mapper.from);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaskEither<IError, void> delete(String id) {
|
||||||
|
return TaskEither.tryCatch(
|
||||||
|
() async {
|
||||||
|
await prisma.userDbo.delete(where: UserDboWhereUniqueInput(id: id));
|
||||||
|
},
|
||||||
|
(error, _) => AppError.databaseError(
|
||||||
|
message: 'Failed to delete user: ${error.toString()}',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaskEither<IError, List<User>> findAll() {
|
||||||
|
return TaskEither<IError, Iterable<UserDbo>>.tryCatch(
|
||||||
|
() async => await prisma.userDbo.findMany(),
|
||||||
|
(error, _) => AppError.databaseError(
|
||||||
|
message: 'Failed to fetch all users: ${error.toString()}',
|
||||||
|
),
|
||||||
|
).flatMap(mapper.listFrom);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaskEither<IError, String> generateId() {
|
||||||
|
return TaskEither.tryCatch(
|
||||||
|
() async {
|
||||||
|
var uuid = Uuid();
|
||||||
|
do {
|
||||||
|
final id = uuid.v4();
|
||||||
|
final user = await prisma.userDbo.findUnique(
|
||||||
|
where: UserDboWhereUniqueInput(id: id),
|
||||||
|
);
|
||||||
|
if (user == null) return id;
|
||||||
|
} while (true);
|
||||||
|
},
|
||||||
|
(error, _) => AppError.databaseError(
|
||||||
|
message: 'Failed to generate ID: ${error.toString()}',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,57 +0,0 @@
|
|||||||
import 'package:backend_dart/domain/entities/user.dart';
|
|
||||||
import 'package:backend_dart/domain/repositories/user_repository.dart';
|
|
||||||
import 'package:backend_dart/infrastructure/persistence/db/prisma.dart';
|
|
||||||
import 'package:orm/orm.dart';
|
|
||||||
|
|
||||||
import 'db/client.dart';
|
|
||||||
|
|
||||||
class PrismaUserRepository implements UserRepository {
|
|
||||||
final PrismaClient prisma;
|
|
||||||
PrismaUserRepository(this.prisma);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> create(User user) async {
|
|
||||||
if (user.password == null) {
|
|
||||||
throw Exception('Password is required');
|
|
||||||
}
|
|
||||||
prisma.userDbo.create(
|
|
||||||
data: PrismaUnion.$1(UserDboCreateInput(
|
|
||||||
id: user.id,
|
|
||||||
name: user.name,
|
|
||||||
email: user.email,
|
|
||||||
password: user.password!,
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<User?> findByEmail(String email) async {
|
|
||||||
final user = await prisma.userDbo
|
|
||||||
.findUnique(where: UserDboWhereUniqueInput(email: email));
|
|
||||||
if (user == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return User(
|
|
||||||
id: user.id!,
|
|
||||||
name: user.name!,
|
|
||||||
email: user.email!,
|
|
||||||
password: user.password,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<User?> findById(String id) async {
|
|
||||||
final user =
|
|
||||||
await prisma.userDbo.findUnique(where: UserDboWhereUniqueInput(id: id));
|
|
||||||
if (user == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return User(
|
|
||||||
id: user.id!,
|
|
||||||
name: user.name!,
|
|
||||||
email: user.email!,
|
|
||||||
password: user.password,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:backend_dart/application/user_service/user_service_provider.dart';
|
import 'package:backend_dart/application/service/user_service_provider.dart';
|
||||||
import 'package:riverpod/riverpod.dart';
|
import 'package:riverpod/riverpod.dart';
|
||||||
import 'package:shelf/shelf.dart';
|
import 'package:shelf/shelf.dart';
|
||||||
import 'package:shelf_router/shelf_router.dart';
|
import 'package:shelf_router/shelf_router.dart';
|
||||||
@ -15,10 +15,10 @@ Router getRouter(ProviderContainer container) {
|
|||||||
return Response.ok('Server is running');
|
return Response.ok('Server is running');
|
||||||
});
|
});
|
||||||
|
|
||||||
// UserService instanzieren
|
// Services
|
||||||
final userService = container.read(userServiceProvider);
|
final userService = container.read(userServiceProvider);
|
||||||
|
|
||||||
// UserService-Router an Haupt-Router binden
|
// UserService-Router
|
||||||
router.mount('/users/', userService.router.call);
|
router.mount('/users/', userService.router.call);
|
||||||
|
|
||||||
return router;
|
return router;
|
||||||
|
@ -799,7 +799,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.0"
|
version: "0.3.0"
|
||||||
uuid:
|
uuid:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: uuid
|
name: uuid
|
||||||
sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff
|
sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff
|
||||||
|
@ -20,6 +20,7 @@ dependencies:
|
|||||||
riverpod_annotation: ^2.6.1
|
riverpod_annotation: ^2.6.1
|
||||||
json_annotation: ^4.9.0
|
json_annotation: ^4.9.0
|
||||||
freezed_annotation: ^2.4.4
|
freezed_annotation: ^2.4.4
|
||||||
|
uuid: ^4.5.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
lints: ^3.0.0
|
lints: ^3.0.0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user