refactor: Minor init refactoring

This commit is contained in:
Christian Pauly 2020-11-19 12:12:15 +01:00
parent b67ab870fa
commit 0697d47cc2
8 changed files with 184 additions and 133 deletions

View File

@ -46,7 +46,9 @@ class Client extends MatrixApi {
int _id;
int get id => _id;
Database database;
final FutureOr<Database> Function(Client) databaseBuilder;
Database _database;
Database get database => _database;
bool enableE2eeRecovery;
@ -87,7 +89,7 @@ class Client extends MatrixApi {
/// in a room for the room list.
Client(
this.clientName, {
this.database,
this.databaseBuilder,
this.enableE2eeRecovery = false,
this.verificationMethods,
http.Client httpClient,
@ -324,7 +326,7 @@ class Client extends MatrixApi {
response.userId == null) {
throw 'Registered but token, device ID or user ID is null.';
}
await connect(
await init(
newToken: response.accessToken,
newUserID: response.userId,
newHomeserver: homeserver,
@ -369,7 +371,7 @@ class Client extends MatrixApi {
loginResp.userId == null) {
throw Exception('Registered but token, device ID or user ID is null.');
}
await connect(
await init(
newToken: loginResp.accessToken,
newUserID: loginResp.userId,
newHomeserver: homeserver,
@ -587,120 +589,159 @@ class Client extends MatrixApi {
/// an error?
int syncErrorTimeoutSec = 3;
/// Sets the user credentials and starts the synchronisation.
///
/// Before you can connect you need at least an [accessToken], a [homeserver],
/// a [userID], a [deviceID], and a [deviceName].
///
/// You get this informations
/// by logging in to your Matrix account, using the [login API](https://matrix.org/docs/spec/client_server/r0.4.0.html#post-matrix-client-r0-login).
///
/// To log in you can use [jsonRequest()] after you have set the [homeserver]
/// to a valid url. For example:
///
/// ```
/// final resp = await matrix
/// .jsonRequest(type: RequestType.POST, action: "/client/r0/login", data: {
/// "type": "m.login.password",
/// "user": "test",
/// "password": "1234",
/// "initial_device_display_name": "Matrix Client"
/// });
/// ```
///
/// Returns:
///
/// ```
/// {
/// "user_id": "@cheeky_monkey:matrix.org",
/// "access_token": "abc123",
/// "device_id": "GHTYAJCE"
/// }
/// ```
///
/// Sends [LoginState.logged] to [onLoginStateChanged].
@Deprecated('Use init() instead')
void connect({
String newToken,
Uri newHomeserver,
String newUserID,
String newDeviceName,
String newDeviceID,
String newPrevBatch,
String newOlmAccount,
}) =>
init(
newToken: newToken,
newHomeserver: newHomeserver,
newUserID: newUserID,
newDeviceName: newDeviceName,
newDeviceID: newDeviceID,
newOlmAccount: newOlmAccount,
);
bool _initLock = false;
/// Sets the user credentials and starts the synchronisation.
///
/// Before you can connect you need at least an [accessToken], a [homeserver],
/// a [userID], a [deviceID], and a [deviceName].
///
/// Usually you don't need to call this method by yourself because [login()], [register()]
/// and even the constructor calls it.
///
/// Sends [LoginState.logged] to [onLoginStateChanged].
///
/// If one of [newToken], [newUserID], [newDeviceID], [newDeviceName] is set then
/// all of them must be set! If you don't set them, this method will try to
/// get them from the database.
void init({
String newToken,
Uri newHomeserver,
String newUserID,
String newDeviceName,
String newDeviceID,
String newOlmAccount,
}) async {
String olmAccount;
if (database != null) {
final account = await database.getClient(clientName);
if (account != null) {
_id = account.clientId;
homeserver = Uri.parse(account.homeserverUrl);
accessToken = account.token;
_userID = account.userId;
_deviceID = account.deviceId;
_deviceName = account.deviceName;
prevBatch = account.prevBatch;
olmAccount = account.olmAccount;
if ((newToken != null ||
newUserID != null ||
newDeviceID != null ||
newDeviceName != null) &&
(newToken == null ||
newUserID == null ||
newDeviceID == null ||
newDeviceName == null)) {
throw Exception(
'If one of [newToken, newUserID, newDeviceID, newDeviceName] is set then all of them must be set!');
}
if (_initLock) throw Exception('[init()] has been called multiple times!');
_initLock = true;
try {
Logs.info('Initialize client $clientName');
if (isLogged()) {
throw Exception('User is already logged in! Call [logout()] first!');
}
}
accessToken = newToken ?? accessToken;
homeserver = newHomeserver ?? homeserver;
_userID = newUserID ?? _userID;
_deviceID = newDeviceID ?? _deviceID;
_deviceName = newDeviceName ?? _deviceName;
prevBatch = newPrevBatch ?? prevBatch;
olmAccount = newOlmAccount ?? olmAccount;
if (accessToken == null || homeserver == null || _userID == null) {
// we aren't logged in
encryption?.dispose();
encryption = null;
onLoginStateChanged.add(LoginState.loggedOut);
return;
}
if (databaseBuilder != null) {
_database ??= await databaseBuilder(this);
}
encryption?.dispose();
encryption =
Encryption(client: this, enableE2eeRecovery: enableE2eeRecovery);
await encryption.init(olmAccount);
if (database != null) {
if (id != null) {
await database.updateClient(
homeserver.toString(),
accessToken,
_userID,
_deviceID,
_deviceName,
prevBatch,
encryption?.pickledOlmAccount,
id,
);
String olmAccount;
if (database != null) {
final account = await database.getClient(clientName);
if (account != null) {
_id = account.clientId;
homeserver = Uri.parse(account.homeserverUrl);
accessToken = account.token;
_userID = account.userId;
_deviceID = account.deviceId;
_deviceName = account.deviceName;
prevBatch = account.prevBatch;
olmAccount = account.olmAccount;
}
}
if (newToken != null) {
accessToken = newToken;
homeserver = newHomeserver;
_userID = newUserID;
_deviceID = newDeviceID;
_deviceName = newDeviceName;
olmAccount = newOlmAccount;
} else {
_id = await database.insertClient(
clientName,
homeserver.toString(),
accessToken,
_userID,
_deviceID,
_deviceName,
prevBatch,
encryption?.pickledOlmAccount,
);
accessToken = newToken ?? accessToken;
homeserver = newHomeserver ?? homeserver;
_userID = newUserID ?? _userID;
_deviceID = newDeviceID ?? _deviceID;
_deviceName = newDeviceName ?? _deviceName;
olmAccount = newOlmAccount ?? olmAccount;
}
_userDeviceKeys = await database.getUserDeviceKeys(this);
_rooms = await database.getRoomList(this, onlyLeft: false);
_sortRooms();
accountData = await database.getAccountData(id);
presences.clear();
if (accessToken == null || homeserver == null || _userID == null) {
// we aren't logged in
encryption?.dispose();
encryption = null;
onLoginStateChanged.add(LoginState.loggedOut);
Logs.info('User is not logged in.');
_initLock = false;
return;
}
_initLock = false;
encryption?.dispose();
encryption =
Encryption(client: this, enableE2eeRecovery: enableE2eeRecovery);
await encryption.init(olmAccount);
if (database != null) {
if (id != null) {
await database.updateClient(
homeserver.toString(),
accessToken,
_userID,
_deviceID,
_deviceName,
prevBatch,
encryption?.pickledOlmAccount,
id,
);
} else {
_id = await database.insertClient(
clientName,
homeserver.toString(),
accessToken,
_userID,
_deviceID,
_deviceName,
prevBatch,
encryption?.pickledOlmAccount,
);
}
_userDeviceKeys = await database.getUserDeviceKeys(this);
_rooms = await database.getRoomList(this, onlyLeft: false);
_sortRooms();
accountData = await database.getAccountData(id);
presences.clear();
}
onLoginStateChanged.add(LoginState.logged);
Logs.success(
'Successfully connected as ${userID.localpart} with ${homeserver.toString()}',
);
return _sync();
} catch (e, s) {
Logs.error('Initialization failed: ${e.toString()}', s);
clear();
onLoginStateChanged.addError(e, s);
rethrow;
}
onLoginStateChanged.add(LoginState.logged);
Logs.success(
'Successfully connected as ${userID.localpart} with ${homeserver.toString()}',
);
// Always do a _sync after login, even if backgroundSync is set to off
return _sync();
}
/// Used for testing only
@ -1629,7 +1670,7 @@ sort order of ${prevState.sortOrder}. This should never happen...''');
/// Stops the synchronization and closes the database. After this
/// you can safely make this Client instance null.
Future<void> dispose({bool closeDatabase = false}) async {
Future<void> dispose({bool closeDatabase = true}) async {
_disposed = true;
try {
await _currentTransaction;
@ -1639,11 +1680,13 @@ sort order of ${prevState.sortOrder}. This should never happen...''');
encryption?.dispose();
encryption = null;
try {
if (closeDatabase) await database?.close();
if (closeDatabase && database != null) {
await database.close().catchError((e, s) => Logs.warning(e, s));
_database = null;
}
} catch (error, stacktrace) {
Logs.warning('Failed to close database: ' + error.toString(), stacktrace);
}
database = null;
return;
}
}

View File

@ -91,7 +91,7 @@ void main() {
var firstSyncFuture = matrix.onFirstSync.stream.first;
var syncFuture = matrix.onSync.stream.first;
matrix.connect(
matrix.init(
newToken: 'abcd',
newUserID: '@test:fakeServer.notExisting',
newHomeserver: matrix.homeserver,
@ -379,10 +379,14 @@ void main() {
});
});
test('Test the fake store api', () async {
var client1 = Client('testclient', httpClient: FakeMatrixApi());
client1.database = getDatabase();
final database = await getDatabase(null);
var client1 = Client(
'testclient',
httpClient: FakeMatrixApi(),
databaseBuilder: (_) => database,
);
client1.connect(
client1.init(
newToken: 'abc123',
newUserID: '@test:fakeServer.notExisting',
newHomeserver: Uri.parse('https://fakeServer.notExisting'),
@ -396,10 +400,13 @@ void main() {
expect(client1.isLogged(), true);
expect(client1.rooms.length, 2);
var client2 = Client('testclient', httpClient: FakeMatrixApi());
client2.database = client1.database;
var client2 = Client(
'testclient',
httpClient: FakeMatrixApi(),
databaseBuilder: (_) => database,
);
client2.connect();
client2.init();
await Future.delayed(Duration(milliseconds: 100));
expect(client2.isLogged(), true);

View File

@ -22,6 +22,7 @@ import 'package:test/test.dart';
import 'package:olm/olm.dart' as olm;
import '../fake_client.dart';
import '../fake_database.dart';
import '../fake_matrix_api.dart';
void main() {
@ -43,15 +44,15 @@ void main() {
if (!olmEnabled) return;
Client client;
var otherClient = Client('othertestclient', httpClient: FakeMatrixApi());
var otherClient = Client('othertestclient',
httpClient: FakeMatrixApi(), databaseBuilder: getDatabase);
DeviceKeys device;
Map<String, dynamic> payload;
test('setupClient', () async {
client = await getClient();
otherClient.database = client.database;
await otherClient.checkHomeserver('https://fakeServer.notExisting');
otherClient.connect(
otherClient.init(
newToken: 'abc',
newUserID: '@othertest:fakeServer.notExisting',
newHomeserver: otherClient.homeserver,

View File

@ -83,10 +83,11 @@ void main() {
test('setupClient', () async {
client1 = await getClient();
client2 = Client('othertestclient', httpClient: FakeMatrixApi());
client2.database = client1.database;
client2 = Client('othertestclient',
httpClient: FakeMatrixApi(),
databaseBuilder: (_) => client1.database);
await client2.checkHomeserver('https://fakeServer.notExisting');
client2.connect(
client2.init(
newToken: 'abc',
newUserID: '@othertest:fakeServer.notExisting',
newHomeserver: client2.homeserver,

View File

@ -29,10 +29,10 @@ const pickledOlmAccount =
'N2v1MkIFGcl0mQpo2OCwSopxPQJ0wnl7oe7PKiT4141AijfdTIhRu+ceXzXKy3Kr00nLqXtRv7kid6hU4a+V0rfJWLL0Y51+3Rp/ORDVnQy+SSeo6Fn4FHcXrxifJEJ0djla5u98fBcJ8BSkhIDmtXRPi5/oJAvpiYn+8zMjFHobOeZUAxYR0VfQ9JzSYBsSovoQ7uFkNks1M4EDUvHtuyg3RxViwdNxs3718fyAqQ/VSwbXsY0Nl+qQbF+nlVGHenGqk5SuNl1P6e1PzZxcR0IfXA94Xij1Ob5gDv5YH4UCn9wRMG0abZsQP0YzpDM0FLaHSCyo9i5JD/vMlhH+nZWrgAzPPCTNGYewNV8/h3c+VyJh8ZTx/fVi6Yq46Fv+27Ga2ETRZ3Qn+Oyx6dLBjnBZ9iUvIhqpe2XqaGA1PopOz8iDnaZitw';
Future<Client> getClient() async {
final client = Client('testclient', httpClient: FakeMatrixApi());
client.database = getDatabase();
final client = Client('testclient',
httpClient: FakeMatrixApi(), databaseBuilder: getDatabase);
await client.checkHomeserver('https://fakeServer.notExisting');
client.connect(
client.init(
newToken: 'abcd',
newUserID: '@test:fakeServer.notExisting',
newHomeserver: client.homeserver,

View File

@ -20,7 +20,7 @@ import 'package:famedlysdk/famedlysdk.dart';
import 'package:moor/moor.dart';
import 'package:moor/ffi.dart' as moor;
Database getDatabase() {
Future<Database> getDatabase(Client _) async {
moorRuntimeOptions.dontWarnAboutMultipleDatabases = true;
return Database(moor.VmDatabase.memory());
}

View File

@ -23,10 +23,10 @@ import 'fake_database.dart';
void main() {
group('Databse', () {
final database = getDatabase();
var clientId = -1;
var room = Room(id: '!room:blubb');
test('setupDatabase', () async {
final database = await getDatabase(null);
clientId = await database.insertClient(
'testclient',
'https://example.org',
@ -38,6 +38,7 @@ void main() {
null);
});
test('storeEventUpdate', () async {
final database = await getDatabase(null);
// store a simple update
var update = EventUpdate(
type: EventUpdateType.timeline,

View File

@ -22,16 +22,14 @@ void test() async {
Logs.success('[LibOlm] Enabled');
Logs.success('++++ Login Alice at ++++');
testClientA = Client('TestClientA');
testClientA.database = getDatabase();
testClientA = Client('TestClientA', databaseBuilder: getDatabase);
await testClientA.checkHomeserver(TestUser.homeserver);
await testClientA.login(
user: TestUser.username, password: TestUser.password);
assert(testClientA.encryptionEnabled);
Logs.success('++++ Login Bob ++++');
testClientB = Client('TestClientB');
testClientB.database = getDatabase();
testClientB = Client('TestClientB', databaseBuilder: getDatabase);
await testClientB.checkHomeserver(TestUser.homeserver);
await testClientB.login(
user: TestUser.username2, password: TestUser.password);
@ -221,7 +219,7 @@ void test() async {
"++++ (Alice) Received decrypted message: '${room.lastMessage}' ++++");
Logs.success('++++ Login Bob in another client ++++');
var testClientC = Client('TestClientC', database: getDatabase());
var testClientC = Client('TestClientC', databaseBuilder: getDatabase);
await testClientC.checkHomeserver(TestUser.homeserver);
await testClientC.login(
user: TestUser.username2, password: TestUser.password);
@ -269,7 +267,7 @@ void test() async {
"++++ (Bob) Received decrypted message: '${inviteRoom.lastMessage}' ++++");
Logs.success('++++ Logout Bob another client ++++');
await testClientC.dispose();
await testClientC.dispose(closeDatabase: false);
await testClientC.logout();
testClientC = null;
await Future.delayed(Duration(seconds: 5));
@ -317,8 +315,8 @@ void test() async {
Logs.success('++++ Logout Alice and Bob ++++');
if (testClientA?.isLogged() ?? false) await testClientA.logoutAll();
if (testClientA?.isLogged() ?? false) await testClientB.logoutAll();
await testClientA?.dispose();
await testClientB?.dispose();
await testClientA?.dispose(closeDatabase: false);
await testClientB?.dispose(closeDatabase: false);
testClientA = null;
testClientB = null;
}