Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(gotrue): Add missing members on User json serialization #512

Merged
merged 10 commits into from
Jun 13, 2023
34 changes: 34 additions & 0 deletions packages/gotrue/lib/src/types/mfa.dart
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,40 @@ class Factor {
updatedAt: DateTime.parse(json['updated_at']),
);
}

Map<String, dynamic> toJson() {
return {
'id': id,
'friendly_name': friendlyName,
'factor_type': factorType.name,
'status': status.name,
'created_at': createdAt.toIso8601String(),
'updated_at': updatedAt.toIso8601String(),
};
}

@override
bool operator ==(Object other) {
if (identical(this, other)) return true;

return other is Factor &&
other.id == id &&
other.friendlyName == friendlyName &&
other.factorType == factorType &&
other.status == status &&
other.createdAt == createdAt &&
other.updatedAt == updatedAt;
}

@override
int get hashCode {
return id.hashCode ^
friendlyName.hashCode ^
factorType.hashCode ^
status.hashCode ^
createdAt.hashCode ^
updatedAt.hashCode;
}
}

enum AuthenticatorAssuranceLevels {
Expand Down
30 changes: 30 additions & 0 deletions packages/gotrue/lib/src/types/session.dart
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,34 @@ class Session {
user: user ?? this.user,
);
}

@override
String toString() {
return 'Session(providerToken: $providerToken, providerRefreshToken: $providerRefreshToken, expiresIn: $expiresIn, tokenType: $tokenType, user: $user, accessToken: $accessToken, refreshToken: $refreshToken)';
}

@override
bool operator ==(Object other) {
if (identical(this, other)) return true;

return other is Session &&
other.providerToken == providerToken &&
other.providerRefreshToken == providerRefreshToken &&
other.accessToken == accessToken &&
other.expiresIn == expiresIn &&
other.refreshToken == refreshToken &&
other.tokenType == tokenType &&
other.user == user;
}

@override
int get hashCode {
return providerToken.hashCode ^
providerRefreshToken.hashCode ^
accessToken.hashCode ^
expiresIn.hashCode ^
refreshToken.hashCode ^
tokenType.hashCode ^
user.hashCode;
}
}
171 changes: 127 additions & 44 deletions packages/gotrue/lib/src/types/user.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// ignore_for_file: deprecated_member_use_from_same_package

import 'package:collection/collection.dart';
import 'package:gotrue/src/types/mfa.dart';

class User {
Expand Down Expand Up @@ -54,54 +57,121 @@ class User {
if (json['id'] == null) {
return null;
}

return User(
id: json['id'] as String,
appMetadata: json['app_metadata'] as Map<String, dynamic>,
id: json['id'] ?? '',
appMetadata: json['app_metadata'] as Map<String, dynamic>? ?? {},
dshukertjr marked this conversation as resolved.
Show resolved Hide resolved
userMetadata: json['user_metadata'] as Map<String, dynamic>?,
aud: json['aud'] as String,
confirmationSentAt: json['confirmation_sent_at'] as String?,
recoverySentAt: json['recovery_sent_at'] as String?,
emailChangeSentAt: json['email_change_sent_at'] as String?,
newEmail: json['new_email'] as String?,
invitedAt: json['invited_at'] as String?,
actionLink: json['action_link'] as String?,
email: json['email'] as String?,
phone: json['phone'] as String?,
createdAt: json['created_at'] as String,
// ignore: deprecated_member_use_from_same_package
confirmedAt: json['confirmed_at'] as String?,
emailConfirmedAt: json['email_confirmed_at'] as String?,
phoneConfirmedAt: json['phone_confirmed_at'] as String?,
lastSignInAt: json['last_sign_in_at'] as String?,
role: json['role'] as String?,
updatedAt: json['updated_at'] as String?,
identities:
(json['identities'] as List?)?.cast<Map<String, dynamic>>().map((e) {
return UserIdentity.fromMap(e);
}).toList(),
factors:
(json['factors'] as List?)?.cast<Map<String, dynamic>>().map((e) {
return Factor.fromJson(e);
}).toList(),
aud: json['aud'] ?? '',
confirmationSentAt: json['confirmation_sent_at'],
recoverySentAt: json['recovery_sent_at'],
emailChangeSentAt: json['email_change_sent_at'],
newEmail: json['new_email'],
invitedAt: json['invited_at'],
actionLink: json['action_link'],
email: json['email'],
phone: json['phone'],
createdAt: json['created_at'] ?? '',
confirmedAt: json['confirmed_at'],
emailConfirmedAt: json['email_confirmed_at'],
phoneConfirmedAt: json['phone_confirmed_at'],
lastSignInAt: json['last_sign_in_at'],
role: json['role'],
updatedAt: json['updated_at'],
identities: json['identities'] != null
? List<UserIdentity>.from(
json['identities']?.map((x) => UserIdentity.fromMap(x)))
: null,
factors: json['factors'] != null
? List<Factor>.from(json['factors']?.map((x) => Factor.fromJson(x)))
: null,
);
}

Map<String, dynamic> toJson() => {
'id': id,
'app_metadata': appMetadata,
'user_metadata': userMetadata,
'aud': aud,
'email': email,
'phone': phone,
'created_at': createdAt,
// ignore: deprecated_member_use_from_same_package
'confirmed_at': confirmedAt,
'email_confirmed_at': emailConfirmedAt,
'phone_confirmed_at': phoneConfirmedAt,
'last_sign_in_at': lastSignInAt,
'role': role,
'updated_at': updatedAt,
};
Map<String, dynamic> toJson() {
return {
'id': id,
'app_metadata': appMetadata,
'user_metadata': userMetadata,
'aud': aud,
'confirmation_sent_at': confirmationSentAt,
'recovery_sent_at': recoverySentAt,
'email_change_sent_at': emailChangeSentAt,
'new_email': newEmail,
'invited_at': invitedAt,
'action_link': actionLink,
'email': email,
'phone': phone,
'created_at': createdAt,
'confirmed_at': confirmedAt,
'email_confirmed_at': emailConfirmedAt,
'phone_confirmed_at': phoneConfirmedAt,
'last_sign_in_at': lastSignInAt,
'role': role,
'updated_at': updatedAt,
'identities': identities?.map((identity) => identity.toJson()).toList(),
'factors': factors?.map((factor) => factor.toJson()).toList(),
};
}

@override
String toString() {
return 'User(id: $id, appMetadata: $appMetadata, userMetadata: $userMetadata, aud: $aud, confirmationSentAt: $confirmationSentAt, recoverySentAt: $recoverySentAt, emailChangeSentAt: $emailChangeSentAt, newEmail: $newEmail, invitedAt: $invitedAt, actionLink: $actionLink, email: $email, phone: $phone, createdAt: $createdAt, confirmedAt: $confirmedAt, emailConfirmedAt: $emailConfirmedAt, phoneConfirmedAt: $phoneConfirmedAt, lastSignInAt: $lastSignInAt, role: $role, updatedAt: $updatedAt, identities: $identities, factors: $factors)';
}

@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
final collectionEquals = const DeepCollectionEquality().equals;

return other is User &&
other.id == id &&
collectionEquals(other.appMetadata, appMetadata) &&
collectionEquals(other.userMetadata, userMetadata) &&
other.aud == aud &&
other.confirmationSentAt == confirmationSentAt &&
other.recoverySentAt == recoverySentAt &&
other.emailChangeSentAt == emailChangeSentAt &&
other.newEmail == newEmail &&
other.invitedAt == invitedAt &&
other.actionLink == actionLink &&
other.email == email &&
other.phone == phone &&
other.createdAt == createdAt &&
other.confirmedAt == confirmedAt &&
other.emailConfirmedAt == emailConfirmedAt &&
other.phoneConfirmedAt == phoneConfirmedAt &&
other.lastSignInAt == lastSignInAt &&
other.role == role &&
other.updatedAt == updatedAt &&
collectionEquals(other.identities, identities) &&
collectionEquals(other.factors, factors);
}

@override
int get hashCode {
return id.hashCode ^
appMetadata.hashCode ^
userMetadata.hashCode ^
aud.hashCode ^
confirmationSentAt.hashCode ^
recoverySentAt.hashCode ^
emailChangeSentAt.hashCode ^
newEmail.hashCode ^
invitedAt.hashCode ^
actionLink.hashCode ^
email.hashCode ^
phone.hashCode ^
createdAt.hashCode ^
confirmedAt.hashCode ^
emailConfirmedAt.hashCode ^
phoneConfirmedAt.hashCode ^
lastSignInAt.hashCode ^
role.hashCode ^
updatedAt.hashCode ^
identities.hashCode ^
factors.hashCode;
}
}

class UserIdentity {
Expand Down Expand Up @@ -155,6 +225,18 @@ class UserIdentity {
);
}

Map<String, dynamic> toJson() {
return {
'id': id,
'user_id': userId,
'identity_data': identityData,
'provider': provider,
'created_at': createdAt,
'last_sign_in_at': lastSignInAt,
'updated_at': updatedAt,
};
}

@override
String toString() {
return 'UserIdentity(id: $id, userId: $userId, identityData: $identityData, provider: $provider, createdAt: $createdAt, lastSignInAt: $lastSignInAt, updatedAt: $updatedAt)';
Expand All @@ -163,11 +245,12 @@ class UserIdentity {
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
final mapEquals = const DeepCollectionEquality().equals;

return other is UserIdentity &&
other.id == id &&
other.userId == userId &&
other.identityData == identityData &&
mapEquals(other.identityData, identityData) &&
other.provider == provider &&
other.createdAt == createdAt &&
other.lastSignInAt == lastSignInAt &&
Expand Down
9 changes: 9 additions & 0 deletions packages/gotrue/test/src/gotrue_mfa_api_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,15 @@ void main() {
totpEntry!.timestamp.difference(DateTime.now()) < Duration(seconds: 2),
true);
});

test('Session object can be properly json serielized', () async {
await client.signInWithPassword(password: password, email: email2);
await client.mfa.challengeAndVerify(factorId: factorId2, code: getTOTP());
final response = await client.refreshSession();
final session = response.session;
final deserializedSession = Session.fromJson(session!.toJson());
expect(session, deserializedSession);
});
}

String getTOTP() {
Expand Down
Loading