diff --git a/client/build.yaml b/client/build.yaml index 5c3643e..4c6ca9c 100644 --- a/client/build.yaml +++ b/client/build.yaml @@ -1,7 +1,6 @@ targets: $default: builders: - moor_generator: + drift_dev: options: generate_connect_constructor: true - use_column_name_as_json_key_when_defined_in_moor_file: false \ No newline at end of file diff --git a/client/lib/musicus_client.dart b/client/lib/musicus_client.dart index 6bcd252..ccea8b4 100644 --- a/client/lib/musicus_client.dart +++ b/client/lib/musicus_client.dart @@ -1,3 +1,2 @@ -export 'src/client.dart'; export 'src/database.dart'; -export 'src/info.dart'; \ No newline at end of file +export 'src/info.dart'; diff --git a/client/lib/src/client.dart b/client/lib/src/client.dart deleted file mode 100644 index e8288c0..0000000 --- a/client/lib/src/client.dart +++ /dev/null @@ -1,631 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:http/http.dart' as http; -import 'package:meta/meta.dart'; - -import 'database.dart'; -import 'info.dart'; - -/// Credentials for a Musicus account. -class MusicusAccountCredentials { - /// The user's username. - final String username; - - /// The user's password. - final String password; - - MusicusAccountCredentials({ - this.username, - this.password, - }); -} - -/// Additional information on a Musicus account. -class MusicusAccountDetails { - /// An optional email address. - final String email; - - MusicusAccountDetails({ - this.email, - }); -} - -/// A simple http client for the Musicus server. -class MusicusClient { - /// URI scheme to use for the connection. - /// - /// This will be used as the scheme parameter when creating Uri objects. - final String scheme; - - /// The host name of the Musicus server to connect to. - /// - /// This will be used as the host parameter when creating Uri objects. - final String host; - - /// This will be used as the port parameter when creating Uri objects. - final int port; - - /// Base path to the root location of the Musicus API. - final String basePath; - - MusicusAccountCredentials _credentials; - - /// Account credentials for login. - /// - /// If this is null, unauthorized requests will fail. - MusicusAccountCredentials get credentials => _credentials; - set credentials(MusicusAccountCredentials credentials) { - _credentials = credentials; - _token = null; - } - - final _client = http.Client(); - - /// The last retrieved access token. - /// - /// If this is null, a new token should be retrieved using [login] if needed. - String _token; - - MusicusClient({ - this.scheme = 'https', - @required this.host, - this.port = 443, - this.basePath, - MusicusAccountCredentials credentials, - }) : assert(scheme != null), - assert(port != null), - assert(host != null), - _credentials = credentials; - - /// Create an URI using member variables and parameters. - Uri createUri({ - @required String path, - Map params, - }) { - return Uri( - scheme: scheme, - host: host, - port: port, - path: basePath != null ? basePath + path : path, - queryParameters: params, - ); - } - - /// Create a new Musicus account. - /// - /// The email address is optional. This will return true, if the action was - /// successful. In that case, the new credentials will automatically be - /// stored as under [credentials] and used for subsequent requests. - Future registerAccount({ - @required String username, - @required String password, - String email, - }) async { - final response = await _client.post( - createUri( - path: '/account/register', - ), - headers: {'Content-Type': 'application/json'}, - body: jsonEncode({ - 'username': username, - 'password': password, - 'email': email, - }), - ); - - if (response.statusCode == HttpStatus.ok) { - _credentials = MusicusAccountCredentials( - username: username, - password: password, - ); - - _token = null; - - return true; - } else { - return false; - } - } - - /// Get the current account details. - Future getAccountDetails() async { - assert(_credentials != null); - - final response = await _authorized( - 'GET', - createUri(path: '/account/details'), - ); - - if (response.statusCode == HttpStatus.ok) { - final json = jsonDecode(response.body); - - return MusicusAccountDetails( - email: json['email'], - ); - } else { - return null; - } - } - - /// Change the account details for the currently used user account. - /// - /// If a parameter is null, it will not be changed. This will throw a - /// [MusicusLoginFailedException] if the account doesn't exist or the old - /// password was wrong. - Future updateAccount({ - String newEmail, - String newPassword, - }) async { - assert(_credentials != null); - - final response = await _client.post( - createUri(path: '/account/details'), - headers: {'Content-Type': 'application/json'}, - body: jsonEncode({ - 'username': _credentials.username, - 'password': _credentials.password, - 'newEmail': newEmail, - 'newPassword': newPassword, - }), - ); - - if (response.statusCode != HttpStatus.ok) { - throw MusicusLoginFailedException(); - } - } - - /// Delete the currently used Musicus account. - /// - /// This will throw a [MusicusLoginFailedException] if the user doesn't exist - /// or the password was wrong. - Future deleteAccount() async { - assert(_credentials != null); - - final response = await _client.post( - createUri(path: '/account/delete'), - headers: {'Content-Type': 'application/json'}, - body: jsonEncode({ - 'username': _credentials.username, - 'password': _credentials.password, - }), - ); - - if (response.statusCode == HttpStatus.ok) { - _credentials = null; - _token = null; - } else { - throw MusicusLoginFailedException(); - } - } - - /// Retrieve an access token for the current user. - /// - /// This will be called automatically, when the client calls a method that - /// requires it. If the login failed, a [MusicusLoginFailedException] will be - /// thrown. - Future login() async { - assert(_credentials != null); - - final response = await _client.post( - createUri( - path: '/account/login', - ), - headers: {'Content-Type': 'application/json'}, - body: jsonEncode({ - 'username': _credentials.username, - 'password': _credentials.password, - }), - ); - - if (response.statusCode == HttpStatus.ok) { - _token = response.body; - } else { - throw MusicusLoginFailedException(); - } - } - - /// Make a request with authorization. - /// - /// This will ensure, that the request will be made with a valid - /// authorization header. If [user] is null, this will throw a - /// [MusicusNotLoggedInException]. If it is neccessary, this will login the - /// user and throw a [MusicusLoginFailedException] if that failed. If the - /// user is not authorized to perform the requested action, this will throw - /// a [MusicusNotAuthorizedException]. - Future _authorized(String method, Uri uri, - {Map headers, String body}) async { - if (_credentials != null) { - Future _request() async { - final request = http.Request(method, uri); - - if (headers != null) { - request.headers.addAll(headers); - } - - request.headers['Authorization'] = 'Bearer $_token'; - - if (body != null) { - request.body = body; - } - - return await http.Response.fromStream(await _client.send(request)); - } - - http.Response response; - - if (_token != null) { - response = await _request(); - if (response.statusCode == HttpStatus.unauthorized) { - await login(); - response = await _request(); - } - } else { - await login(); - response = await _request(); - } - - if (response.statusCode == HttpStatus.forbidden) { - throw MusicusNotAuthorizedException(); - } else { - return response; - } - } else { - throw MusicusNotLoggedInException(); - } - } - - /// Get a list of persons. - /// - /// You can get another page using the [page] parameter. If a non empty - /// [search] string is provided, the persons will get filtered based on that - /// string. - Future> getPersons([int page, String search]) async { - final params = {}; - - if (page != null) { - params['p'] = page.toString(); - } - - if (search != null) { - params['s'] = search; - } - - final response = await _client.get(createUri( - path: '/persons', - params: params, - )); - - final json = jsonDecode(response.body); - return json - .map((j) => Person.fromJson(j).copyWith( - sync: true, - synced: true, - )) - .toList(); - } - - /// Get a person by ID. - Future getPerson(int id) async { - final response = await _client.get(createUri( - path: '/persons/$id', - )); - - final json = jsonDecode(response.body); - return Person.fromJson(json).copyWith( - sync: true, - synced: true, - ); - } - - /// Delete a person by ID. - Future deletePerson(int id) async { - await _authorized( - 'DELETE', - createUri( - path: '/persons/$id', - ), - ); - } - - /// Create or update a person. - /// - /// Returns true, if the operation was successful. - Future putPerson(Person person) async { - final response = await _authorized( - 'PUT', - createUri( - path: '/persons/${person.id}', - ), - headers: {'Content-Type': 'application/json'}, - body: jsonEncode(person.toJson()), - ); - - return response.statusCode == HttpStatus.ok; - } - - /// Get a list of instruments. - /// - /// You can get another page using the [page] parameter. If a non empty - /// [search] string is provided, the results will get filtered based on that - /// string. - Future> getInstruments([int page, String search]) async { - final params = {}; - - if (page != null) { - params['p'] = page.toString(); - } - - if (search != null) { - params['s'] = search; - } - - final response = await _client.get(createUri( - path: '/instruments', - params: params, - )); - - final json = jsonDecode(response.body); - return json - .map((j) => Instrument.fromJson(j).copyWith( - sync: true, - synced: true, - )) - .toList(); - } - - /// Get an instrument by ID. - Future getInstrument(int id) async { - final response = await _client.get(createUri( - path: '/instruments/$id', - )); - - final json = jsonDecode(response.body); - return Instrument.fromJson(json).copyWith( - sync: true, - synced: true, - ); - } - - /// Create or update an instrument. - /// - /// Returns true, if the operation was successful. - Future putInstrument(Instrument instrument) async { - final response = await _authorized( - 'PUT', - createUri( - path: '/instruments/${instrument.id}', - ), - headers: {'Content-Type': 'application/json'}, - body: jsonEncode(instrument.toJson()), - ); - - return response.statusCode == HttpStatus.ok; - } - - /// Delete an instrument by ID. - Future deleteInstrument(int id) async { - await _authorized( - 'DELETE', - createUri( - path: '/instruments/$id', - ), - ); - } - - /// Get a list of works written by the person with the ID [personId]. - /// - /// You can get another page using the [page] parameter. If a non empty - /// [search] string is provided, the results will get filtered based on that - /// string. - Future> getWorks(int personId, - [int page, String search]) async { - final params = {}; - - if (page != null) { - params['p'] = page.toString(); - } - - if (search != null) { - params['s'] = search; - } - - final response = await _client.get(createUri( - path: '/persons/$personId/works', - params: params, - )); - - final json = jsonDecode(response.body); - return json.map((j) => WorkInfo.fromJson(j, sync: true)).toList(); - } - - /// Get a work by ID. - Future getWork(int id) async { - final response = await _client.get(createUri( - path: '/works/$id', - )); - - final json = jsonDecode(response.body); - return WorkInfo.fromJson(json, sync: true); - } - - /// Delete a work by ID. - Future deleteWork(int id) async { - await _authorized( - 'DELETE', - createUri( - path: '/works/$id', - ), - ); - } - - /// Get a list of recordings of the work with the ID [workId]. - /// - /// You can get another page using the [page] parameter. - Future> getRecordings(int workId, [int page]) async { - final params = {}; - - if (page != null) { - params['p'] = page.toString(); - } - - final response = await _client.get(createUri( - path: '/works/$workId/recordings', - params: params, - )); - - final json = jsonDecode(response.body); - return json - .map((j) => RecordingInfo.fromJson(j, sync: true)) - .toList(); - } - - /// Create or update a work. - /// - /// Returns true, if the operation was successful. - Future putWork(WorkInfo workInfo) async { - final response = await _authorized( - 'PUT', - createUri( - path: '/works/${workInfo.work.id}', - ), - headers: {'Content-Type': 'application/json'}, - body: jsonEncode(workInfo.toJson()), - ); - - return response.statusCode == HttpStatus.ok; - } - - /// Get a list of ensembles. - /// - /// You can get another page using the [page] parameter. If a non empty - /// [search] string is provided, the results will get filtered based on that - /// string. - Future> getEnsembles([int page, String search]) async { - final params = {}; - - if (page != null) { - params['p'] = page.toString(); - } - - if (search != null) { - params['s'] = search; - } - - final response = await _client.get(createUri( - path: '/ensembles', - params: params, - )); - - final json = jsonDecode(response.body); - return json - .map((j) => Ensemble.fromJson(j).copyWith( - sync: true, - synced: true, - )) - .toList(); - } - - /// Get an ensemble by ID. - Future getEnsemble(int id) async { - final response = await _client.get(createUri( - path: '/ensembles/$id', - )); - - final json = jsonDecode(response.body); - return Ensemble.fromJson(json).copyWith( - sync: true, - synced: true, - ); - } - - /// Create or update an ensemble. - /// - /// Returns true, if the operation was successful. - Future putEnsemble(Ensemble ensemble) async { - final response = await _authorized( - 'PUT', - createUri( - path: '/ensembles/${ensemble.id}', - ), - headers: {'Content-Type': 'application/json'}, - body: jsonEncode(ensemble.toJson()), - ); - - return response.statusCode == HttpStatus.ok; - } - - /// Delete an ensemble by ID. - Future deleteEnsemble(int id) async { - await _authorized( - 'DELETE', - createUri( - path: '/ensembles/$id', - ), - ); - } - - /// Get a recording by ID. - Future getRecording(int id) async { - final response = await _client.get(createUri( - path: '/recordings/$id', - )); - - final json = jsonDecode(response.body); - return RecordingInfo.fromJson(json, sync: true); - } - - /// Create or update a recording. - /// - /// Returns true, if the operation was successful. - Future putRecording(RecordingInfo recordingInfo) async { - final response = await _authorized( - 'PUT', - createUri( - path: '/recordings/${recordingInfo.recording.id}', - ), - headers: {'Content-Type': 'application/json'}, - body: jsonEncode(recordingInfo.toJson()), - ); - - return response.statusCode == HttpStatus.ok; - } - - /// Delete a recording by ID. - Future deleteRecording(int id) async { - await _authorized( - 'DELETE', - createUri( - path: '/recordings/$id', - ), - ); - } - - /// Close the internal http client. - void dispose() { - _client.close(); - } -} - -class MusicusLoginFailedException implements Exception { - MusicusLoginFailedException(); - - String toString() => 'MusicusLoginFailedException: The username or password ' - 'was wrong.'; -} - -class MusicusNotLoggedInException implements Exception { - MusicusNotLoggedInException(); - - String toString() => - 'MusicusNotLoggedInException: The user must be logged in to perform ' - 'this action.'; -} - -class MusicusNotAuthorizedException implements Exception { - MusicusNotAuthorizedException(); - - String toString() => - 'MusicusNotAuthorizedException: The logged in user is not allowed to ' - 'perform this action.'; -} diff --git a/client/lib/src/database.dart b/client/lib/src/database.dart index 40f3ba8..f8a5e0f 100644 --- a/client/lib/src/database.dart +++ b/client/lib/src/database.dart @@ -1,7 +1,6 @@ import 'dart:math'; -import 'package:moor/moor.dart'; -import 'package:musicus_client/musicus_client.dart'; +import 'package:drift/drift.dart'; import 'info.dart'; @@ -15,16 +14,11 @@ int generateId() => _random.nextInt(0xFFFFFFFF); /// The database for storing all metadata for the music library. /// /// This also handles synchronization with a Musicus server. -@UseMoor(include: {'database.moor'}) +@DriftDatabase(include: {'database.drift'}) class MusicusClientDatabase extends _$MusicusClientDatabase { /// The number of items contained in one result page. static const pageSize = 50; - /// The client to use for synchronization. - /// - /// This may be null indicating that everything should be kept local. - MusicusClient client; - @override int get schemaVersion => 1; @@ -37,63 +31,12 @@ class MusicusClientDatabase extends _$MusicusClientDatabase { MusicusClientDatabase({ @required QueryExecutor executor, - this.client, }) : super(executor); MusicusClientDatabase.connect({ @required DatabaseConnection connection, - this.client, }) : super.connect(connection); - /// Upload all changes to the server. - /// - /// If [update] is true, this will also update existing items with new data - /// from the server. - Future sync([bool update = false]) async { - if (update) { - for (final person in await oldSyncPersons().get()) { - await updatePerson(await client.getPerson(person.id)); - } - for (final instrument in await oldSyncInstruments().get()) { - await updateInstrument(await client.getInstrument(instrument.id)); - } - for (final work in await oldSyncWorks().get()) { - final workInfo = await client.getWork(work.id); - await updateWork(workInfo); - } - for (final ensemble in await oldSyncEnsembles().get()) { - await updateEnsemble(await client.getEnsemble(ensemble.id)); - } - for (final recording in await oldSyncRecordings().get()) { - final recordingInfo = await client.getRecording(recording.id); - await updateRecording(recordingInfo); - } - } - - for (final person in await newSyncPersons().get()) { - await client.putPerson(person); - await updatePerson(person.copyWith(synced: true)); - } - for (final instrument in await newSyncInstruments().get()) { - await client.putInstrument(instrument); - await updateInstrument(instrument.copyWith(synced: true)); - } - for (final work in await newSyncWorks().get()) { - final workInfo = await getWorkInfo(work); - await client.putWork(workInfo); - await into(works).insertOnConflictUpdate(work.copyWith(synced: true)); - } - for (final ensemble in await newSyncEnsembles().get()) { - await client.putEnsemble(ensemble); - await updateEnsemble(ensemble.copyWith(synced: true)); - } - for (final recording in await newSyncRecordings().get()) { - final recordingInfo = await getRecordingInfo(recording); - await client.putRecording(recordingInfo); - await into(recordings) - .insertOnConflictUpdate(recording.copyWith(synced: true)); - } - } /// Get all available persons. /// @@ -113,27 +56,6 @@ class MusicusClientDatabase extends _$MusicusClientDatabase { return result; } - /// Add [person] or replace an existing person with the same ID. - Future updatePerson(Person person) async { - await into(persons).insert( - person, - mode: InsertMode.insertOrReplace, - ); - } - - /// Delete the person by [id]. - /// - /// If [sync] is true, the person will be deleted from the server too. If - /// that fails, a MusicusNotAuthorizedException or MusicusNotLoggedInException - /// willl be thrown and the person will NOT be deleted. - Future deletePerson(int id, [bool sync = false]) async { - if (sync) { - await client.deletePerson(id); - } - - await (delete(persons)..where((p) => p.id.equals(id))).go(); - } - /// Get all available instruments. /// /// This will return a list of [pageSize] instruments. You can get another @@ -152,27 +74,6 @@ class MusicusClientDatabase extends _$MusicusClientDatabase { return result; } - /// Add [instrument] or replace an existing one with the same ID. - Future updateInstrument(Instrument instrument) async { - await into(instruments).insert( - instrument, - mode: InsertMode.insertOrReplace, - ); - } - - /// Delete the instrument by [id]. - /// - /// If [sync] is true, the instrument will be deleted from the server too. If - /// that fails, a MusicusNotAuthorizedException or MusicusNotLoggedInException - /// willl be thrown and the instrument will NOT be deleted. - Future deleteInstrument(int id, [bool sync = false]) async { - if (sync) { - await client.deleteInstrument(id); - } - - await (delete(instruments)..where((i) => i.id.equals(id))).go(); - } - /// Retrieve more information on an already queried work. Future getWorkInfo(Work work) async { final id = work.id; @@ -238,74 +139,6 @@ class MusicusClientDatabase extends _$MusicusClientDatabase { return result; } - /// Add or replace a work and its associated data. - /// - /// This will explicitly update all associated composers and instruments, even - /// if they have already existed before. - Future updateWork(WorkInfo workInfo) async { - await transaction(() async { - final workId = workInfo.work.id; - - // Delete data associated rows in other tables first. We can't just - // delete the work itself, because that would violate the foreign key - // constraints. - await (delete(instrumentations)..where((i) => i.work.equals(workId))) - .go(); - await (delete(workParts)..where((p) => p.partOf.equals(workId))).go(); - await (delete(workSections)..where((s) => s.work.equals(workId))).go(); - - // This will also include the composers of the work's parts. - for (final person in workInfo.composers) { - await updatePerson(person); - } - - await into(works).insert(workInfo.work, mode: InsertMode.insertOrReplace); - - // At the moment, this will also update all provided instruments, even if - // they were already there previously. - for (final instrument in workInfo.instruments) { - await updateInstrument(instrument); - await into(instrumentations).insert(Instrumentation( - work: workId, - instrument: instrument.id, - )); - } - - for (final partInfo in workInfo.parts) { - final part = partInfo.part; - - await into(workParts).insert(part); - - for (final instrument in workInfo.instruments) { - await updateInstrument(instrument); - await into(partInstrumentations).insert(PartInstrumentation( - workPart: part.id, - instrument: instrument.id, - )); - } - } - - for (final section in workInfo.sections) { - await into(workSections).insert(section); - } - }); - } - - /// Delete the work by [id]. - /// - /// If [sync] is true, the work will be deleted from the server too. If - /// that fails, a MusicusNotAuthorizedException or MusicusNotLoggedInException - /// willl be thrown and the work will NOT be deleted. - Future deleteWork(int id, [bool sync = false]) async { - if (sync) { - await client.deleteWork(id); - } - - // The parts and instrumentations will be deleted automatically due to - // their foreign key constraints. - await (delete(works)..where((w) => w.id.equals(id))).go(); - } - /// Get all available ensembles. /// /// This will return a list of [pageSize] ensembles. You can get another page @@ -324,63 +157,6 @@ class MusicusClientDatabase extends _$MusicusClientDatabase { return result; } - /// Add [ensemble] or replace an existing one with the same ID. - Future updateEnsemble(Ensemble ensemble) async { - await into(ensembles).insert( - ensemble, - mode: InsertMode.insertOrReplace, - ); - } - - /// Delete the ensemble by [id]. - /// - /// If [sync] is true, the ensemble will be deleted from the server too. If - /// that fails, a MusicusNotAuthorizedException or MusicusNotLoggedInException - /// willl be thrown and the ensemble will NOT be deleted. - Future deleteEnsemble(int id, [bool sync = false]) async { - if (sync) { - await client.deleteEnsemble(id); - } - - await (delete(ensembles)..where((e) => e.id.equals(id))).go(); - } - - /// Add or replace a recording and its associated data. - /// - /// This will explicitly also update all assoicated persons and instruments. - Future updateRecording(RecordingInfo recordingInfo) async { - await transaction(() async { - final recordingId = recordingInfo.recording.id; - - // Delete the old recording first. This will also delete the performances - // due to their foreign key constraint. - await deleteRecording(recordingId); - - await into(recordings).insert(recordingInfo.recording); - - for (final performance in recordingInfo.performances) { - if (performance.person != null) { - await updatePerson(performance.person); - } - - if (performance.ensemble != null) { - await updateEnsemble(performance.ensemble); - } - - if (performance.role != null) { - await updateInstrument(performance.role); - } - - await into(performances).insert(Performance( - recording: recordingId, - person: performance.person?.id, - ensemble: performance.ensemble?.id, - role: performance.role?.id, - )); - } - }); - } - /// Retreive more information on an already queried recording. Future getRecordingInfo(Recording recording) async { final id = recording.id; @@ -412,21 +188,6 @@ class MusicusClientDatabase extends _$MusicusClientDatabase { return await getRecordingInfo(recording); } - /// Delete a recording by [id]. - /// - /// If [sync] is true, the recording will be deleted from the server too. If - /// that fails, a MusicusNotAuthorizedException or MusicusNotLoggedInException - /// willl be thrown and the recording will NOT be deleted. - Future deleteRecording(int id, [bool sync = false]) async { - if (sync) { - await client.deleteRecording(id); - } - - // This will also delete the performances due to their foreign key - // constraint. - await (delete(recordings)..where((r) => r.id.equals(id))).go(); - } - /// Get information on all recordings of the work with ID [workId]. /// /// This will return a list of [pageSize] recordings. You can get the other diff --git a/client/lib/src/database.moor b/client/lib/src/database.drift similarity index 73% rename from client/lib/src/database.moor rename to client/lib/src/database.drift index 037006c..612255e 100644 --- a/client/lib/src/database.moor +++ b/client/lib/src/database.drift @@ -1,26 +1,20 @@ CREATE TABLE persons ( id INTEGER NOT NULL PRIMARY KEY, first_name TEXT NOT NULL, - last_name TEXT NOT NULL, - sync BOOLEAN NOT NULL DEFAULT FALSE, - synced BOOLEAN NOT NULL DEFAULT FALSE + last_name TEXT NOT NULL ); -- This represents real instruments as well as other roles that can be played -- in a recording. CREATE TABLE instruments ( id INTEGER NOT NULL PRIMARY KEY, - name TEXT NOT NULL, - sync BOOLEAN NOT NULL DEFAULT FALSE, - synced BOOLEAN NOT NULL DEFAULT FALSE + name TEXT NOT NULL ); CREATE TABLE works ( id INTEGER NOT NULL PRIMARY KEY, composer INTEGER REFERENCES persons(id) ON DELETE SET NULL, - title TEXT NOT NULL, - sync BOOLEAN NOT NULL DEFAULT FALSE, - synced BOOLEAN NOT NULL DEFAULT FALSE + title TEXT NOT NULL ); CREATE TABLE instrumentations ( @@ -50,17 +44,13 @@ CREATE TABLE work_sections ( CREATE TABLE ensembles ( id INTEGER NOT NULL PRIMARY KEY, - name TEXT NOT NULL, - sync BOOLEAN NOT NULL DEFAULT FALSE, - synced BOOLEAN NOT NULL DEFAULT FALSE + name TEXT NOT NULL ); CREATE TABLE recordings ( id INTEGER NOT NULL PRIMARY KEY, work INTEGER REFERENCES works(id) ON DELETE SET NULL, - comment TEXT NOT NULL, - sync BOOLEAN NOT NULL DEFAULT FALSE, - synced BOOLEAN NOT NULL DEFAULT FALSE + comment TEXT NOT NULL ); CREATE TABLE performances ( @@ -74,12 +64,6 @@ allPersons: SELECT * FROM persons ORDER BY last_name, first_name LIMIT :limit OFFSET :offset; -newSyncPersons: -SELECT * FROM persons WHERE sync = TRUE AND synced = FALSE; - -oldSyncPersons: -SELECT * FROM persons WHERE sync = TRUE AND synced = TRUE; - searchPersons: SELECT * FROM persons WHERE last_name LIKE :search ORDER BY last_name, first_name LIMIT :limit OFFSET :offset; @@ -90,12 +74,6 @@ SELECT * FROM persons WHERE id = :id LIMIT 1; allInstruments: SELECT * FROM instruments ORDER BY name LIMIT :limit OFFSET :offset; -newSyncInstruments: -SELECT * FROM instruments WHERE sync = TRUE AND synced = FALSE; - -oldSyncInstruments: -SELECT * FROM instruments WHERE sync = TRUE AND synced = TRUE; - searchInstruments: SELECT * FROM instruments WHERE name LIKE :search ORDER BY name LIMIT :limit OFFSET :offset; @@ -103,12 +81,6 @@ SELECT * FROM instruments WHERE name LIKE :search ORDER BY name instrumentById: SELECT * FROM instruments WHERE id = :id LIMIT 1; -newSyncWorks: -SELECT * FROM works WHERE sync = TRUE AND synced = FALSE; - -oldSyncWorks: -SELECT * FROM works WHERE sync = TRUE AND synced = TRUE; - workById: SELECT * FROM works WHERE id = :id LIMIT 1; @@ -149,12 +121,6 @@ SELECT instruments.* FROM part_instrumentations allEnsembles: SELECT * FROM ensembles ORDER BY name LIMIT :limit OFFSET :offset; -newSyncEnsembles: -SELECT * FROM ensembles WHERE sync = TRUE AND synced = FALSE; - -oldSyncEnsembles: -SELECT * FROM ensembles WHERE sync = TRUE AND synced = TRUE; - searchEnsembles: SELECT * FROM ensembles WHERE name LIKE :search ORDER BY name LIMIT :limit OFFSET :offset; @@ -162,12 +128,6 @@ SELECT * FROM ensembles WHERE name LIKE :search ORDER BY name ensembleById: SELECT * FROM ensembles WHERE id = :id LIMIT 1; -newSyncRecordings: -SELECT * FROM recordings WHERE sync = TRUE AND synced = FALSE; - -oldSyncRecordings: -SELECT * FROM recordings WHERE sync = TRUE AND synced = TRUE; - recordingById: SELECT * FROM recordings WHERE id = :id; @@ -176,4 +136,4 @@ SELECT * FROM recordings WHERE work = :id ORDER BY id LIMIT :limit OFFSET :offset; performancesByRecording: -SELECT * FROM performances WHERE recording = :id; \ No newline at end of file +SELECT * FROM performances WHERE recording = :id; diff --git a/client/lib/src/info.dart b/client/lib/src/info.dart index c9bd3bf..74a9333 100644 --- a/client/lib/src/info.dart +++ b/client/lib/src/info.dart @@ -20,21 +20,6 @@ class PartInfo { this.instruments, this.composer, }); - - factory PartInfo.fromJson(Map json) => PartInfo( - part: WorkPart.fromJson(json['part']), - instruments: json['instruments'] - .map((j) => Instrument.fromJson(j)) - .toList(), - composer: - json['composer'] != null ? Person.fromJson(json['composer']) : null, - ); - - Map toJson() => { - 'part': part.toJson(), - 'instruments': instruments.map((i) => i.toJson()).toList(), - 'composers': composer?.toJson(), - }; } /// A bundle information on a work. @@ -66,45 +51,6 @@ class WorkInfo { this.parts, this.sections, }); - - /// Deserialize work info from JSON. - /// - /// If [sync] is set to true, all contained items will have their sync - /// property set to true. - // TODO: Local versions should not be overriden, if their sync property is - // set to false. - factory WorkInfo.fromJson(Map json, {bool sync = false}) => - WorkInfo( - work: Work.fromJson(json['work']).copyWith( - sync: sync, - synced: sync, - ), - instruments: json['instruments'] - .map((j) => Instrument.fromJson(j).copyWith( - sync: sync, - synced: sync, - )) - .toList(), - composers: json['composers'] - .map((j) => Person.fromJson(j).copyWith( - sync: sync, - synced: sync, - )) - .toList(), - parts: - json['parts'].map((j) => PartInfo.fromJson(j)).toList(), - sections: json['sections'] - .map((j) => WorkSection.fromJson(j)) - .toList(), - ); - - Map toJson() => { - 'work': work.toJson(), - 'instruments': instruments.map((i) => i.toJson()).toList(), - 'composers': composers.map((c) => c.toJson()).toList(), - 'parts': parts.map((c) => c.toJson()).toList(), - 'sections': sections.map((s) => s.toJson()).toList(), - }; } /// All available information on a performance within a recording. @@ -127,35 +73,6 @@ class PerformanceInfo { this.ensemble, this.role, }); - - factory PerformanceInfo.fromJson(Map json, - {bool sync = false}) => - PerformanceInfo( - person: json['person'] != null - ? Person.fromJson(json['person']).copyWith( - sync: sync, - synced: sync, - ) - : null, - ensemble: json['ensemble'] != null - ? Ensemble.fromJson(json['ensemble']).copyWith( - sync: sync, - synced: sync, - ) - : null, - role: json['role'] != null - ? Instrument.fromJson(json['role']).copyWith( - sync: sync, - synced: sync, - ) - : null, - ); - - Map toJson() => { - 'person': person?.toJson(), - 'ensemble': ensemble?.toJson(), - 'role': role?.toJson(), - }; } /// All available information on a recording. @@ -173,28 +90,4 @@ class RecordingInfo { this.recording, this.performances, }); - - /// Deserialize recording info from JSON. - /// - /// If [sync] is set to true, all contained items will have their sync - /// property set to true. - // TODO: Local versions should not be overriden, if their sync property is - // set to false. - factory RecordingInfo.fromJson(Map json, - {bool sync = false}) => - RecordingInfo( - recording: Recording.fromJson(json['recording']).copyWith( - sync: sync, - synced: sync, - ), - performances: json['performances'] - .map( - (j) => PerformanceInfo.fromJson(j, sync: sync)) - .toList(), - ); - - Map toJson() => { - 'recording': recording.toJson(), - 'performances': performances.map((p) => p.toJson()).toList(), - }; } diff --git a/client/pubspec.yaml b/client/pubspec.yaml index 39279ff..c8fce41 100644 --- a/client/pubspec.yaml +++ b/client/pubspec.yaml @@ -6,10 +6,8 @@ environment: sdk: ">=2.6.0 <3.0.0" dependencies: - http: - meta: - moor: + drift: ^1.0.0 dev_dependencies: - build_runner: - moor_generator: \ No newline at end of file + build_runner: ^2.1.10 + drift_dev: ^1.0.0