mirror of
https://github.com/johrpan/musicus_mobile.git
synced 2025-10-26 10:47:25 +01:00
client: Remove sync code and update moor to drift
This commit is contained in:
parent
b36fe340ad
commit
289f480703
7 changed files with 13 additions and 1034 deletions
|
|
@ -1,7 +1,6 @@
|
||||||
targets:
|
targets:
|
||||||
$default:
|
$default:
|
||||||
builders:
|
builders:
|
||||||
moor_generator:
|
drift_dev:
|
||||||
options:
|
options:
|
||||||
generate_connect_constructor: true
|
generate_connect_constructor: true
|
||||||
use_column_name_as_json_key_when_defined_in_moor_file: false
|
|
||||||
|
|
@ -1,3 +1,2 @@
|
||||||
export 'src/client.dart';
|
|
||||||
export 'src/database.dart';
|
export 'src/database.dart';
|
||||||
export 'src/info.dart';
|
export 'src/info.dart';
|
||||||
|
|
|
||||||
|
|
@ -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<String, String> 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<bool> 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<MusicusAccountDetails> 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<void> 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<void> 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<void> 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<http.Response> _authorized(String method, Uri uri,
|
|
||||||
{Map<String, String> headers, String body}) async {
|
|
||||||
if (_credentials != null) {
|
|
||||||
Future<http.Response> _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<List<Person>> getPersons([int page, String search]) async {
|
|
||||||
final params = <String, String>{};
|
|
||||||
|
|
||||||
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<Person>((j) => Person.fromJson(j).copyWith(
|
|
||||||
sync: true,
|
|
||||||
synced: true,
|
|
||||||
))
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a person by ID.
|
|
||||||
Future<Person> 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<void> deletePerson(int id) async {
|
|
||||||
await _authorized(
|
|
||||||
'DELETE',
|
|
||||||
createUri(
|
|
||||||
path: '/persons/$id',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create or update a person.
|
|
||||||
///
|
|
||||||
/// Returns true, if the operation was successful.
|
|
||||||
Future<bool> 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<List<Instrument>> getInstruments([int page, String search]) async {
|
|
||||||
final params = <String, String>{};
|
|
||||||
|
|
||||||
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<Instrument>((j) => Instrument.fromJson(j).copyWith(
|
|
||||||
sync: true,
|
|
||||||
synced: true,
|
|
||||||
))
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get an instrument by ID.
|
|
||||||
Future<Instrument> 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<bool> 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<void> 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<List<WorkInfo>> getWorks(int personId,
|
|
||||||
[int page, String search]) async {
|
|
||||||
final params = <String, String>{};
|
|
||||||
|
|
||||||
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<WorkInfo>((j) => WorkInfo.fromJson(j, sync: true)).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a work by ID.
|
|
||||||
Future<WorkInfo> 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<void> 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<List<RecordingInfo>> getRecordings(int workId, [int page]) async {
|
|
||||||
final params = <String, String>{};
|
|
||||||
|
|
||||||
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<RecordingInfo>((j) => RecordingInfo.fromJson(j, sync: true))
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create or update a work.
|
|
||||||
///
|
|
||||||
/// Returns true, if the operation was successful.
|
|
||||||
Future<bool> 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<List<Ensemble>> getEnsembles([int page, String search]) async {
|
|
||||||
final params = <String, String>{};
|
|
||||||
|
|
||||||
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<Ensemble>((j) => Ensemble.fromJson(j).copyWith(
|
|
||||||
sync: true,
|
|
||||||
synced: true,
|
|
||||||
))
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get an ensemble by ID.
|
|
||||||
Future<Ensemble> 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<bool> 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<void> deleteEnsemble(int id) async {
|
|
||||||
await _authorized(
|
|
||||||
'DELETE',
|
|
||||||
createUri(
|
|
||||||
path: '/ensembles/$id',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a recording by ID.
|
|
||||||
Future<RecordingInfo> 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<bool> 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<void> 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.';
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:moor/moor.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:musicus_client/musicus_client.dart';
|
|
||||||
|
|
||||||
import 'info.dart';
|
import 'info.dart';
|
||||||
|
|
||||||
|
|
@ -15,16 +14,11 @@ int generateId() => _random.nextInt(0xFFFFFFFF);
|
||||||
/// The database for storing all metadata for the music library.
|
/// The database for storing all metadata for the music library.
|
||||||
///
|
///
|
||||||
/// This also handles synchronization with a Musicus server.
|
/// This also handles synchronization with a Musicus server.
|
||||||
@UseMoor(include: {'database.moor'})
|
@DriftDatabase(include: {'database.drift'})
|
||||||
class MusicusClientDatabase extends _$MusicusClientDatabase {
|
class MusicusClientDatabase extends _$MusicusClientDatabase {
|
||||||
/// The number of items contained in one result page.
|
/// The number of items contained in one result page.
|
||||||
static const pageSize = 50;
|
static const pageSize = 50;
|
||||||
|
|
||||||
/// The client to use for synchronization.
|
|
||||||
///
|
|
||||||
/// This may be null indicating that everything should be kept local.
|
|
||||||
MusicusClient client;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get schemaVersion => 1;
|
int get schemaVersion => 1;
|
||||||
|
|
||||||
|
|
@ -37,63 +31,12 @@ class MusicusClientDatabase extends _$MusicusClientDatabase {
|
||||||
|
|
||||||
MusicusClientDatabase({
|
MusicusClientDatabase({
|
||||||
@required QueryExecutor executor,
|
@required QueryExecutor executor,
|
||||||
this.client,
|
|
||||||
}) : super(executor);
|
}) : super(executor);
|
||||||
|
|
||||||
MusicusClientDatabase.connect({
|
MusicusClientDatabase.connect({
|
||||||
@required DatabaseConnection connection,
|
@required DatabaseConnection connection,
|
||||||
this.client,
|
|
||||||
}) : super.connect(connection);
|
}) : 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<void> 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.
|
/// Get all available persons.
|
||||||
///
|
///
|
||||||
|
|
@ -113,27 +56,6 @@ class MusicusClientDatabase extends _$MusicusClientDatabase {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add [person] or replace an existing person with the same ID.
|
|
||||||
Future<void> 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<void> 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.
|
/// Get all available instruments.
|
||||||
///
|
///
|
||||||
/// This will return a list of [pageSize] instruments. You can get another
|
/// This will return a list of [pageSize] instruments. You can get another
|
||||||
|
|
@ -152,27 +74,6 @@ class MusicusClientDatabase extends _$MusicusClientDatabase {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add [instrument] or replace an existing one with the same ID.
|
|
||||||
Future<void> 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<void> 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.
|
/// Retrieve more information on an already queried work.
|
||||||
Future<WorkInfo> getWorkInfo(Work work) async {
|
Future<WorkInfo> getWorkInfo(Work work) async {
|
||||||
final id = work.id;
|
final id = work.id;
|
||||||
|
|
@ -238,74 +139,6 @@ class MusicusClientDatabase extends _$MusicusClientDatabase {
|
||||||
return result;
|
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<void> 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<void> 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.
|
/// Get all available ensembles.
|
||||||
///
|
///
|
||||||
/// This will return a list of [pageSize] ensembles. You can get another page
|
/// This will return a list of [pageSize] ensembles. You can get another page
|
||||||
|
|
@ -324,63 +157,6 @@ class MusicusClientDatabase extends _$MusicusClientDatabase {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add [ensemble] or replace an existing one with the same ID.
|
|
||||||
Future<void> 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<void> 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<void> 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.
|
/// Retreive more information on an already queried recording.
|
||||||
Future<RecordingInfo> getRecordingInfo(Recording recording) async {
|
Future<RecordingInfo> getRecordingInfo(Recording recording) async {
|
||||||
final id = recording.id;
|
final id = recording.id;
|
||||||
|
|
@ -412,21 +188,6 @@ class MusicusClientDatabase extends _$MusicusClientDatabase {
|
||||||
return await getRecordingInfo(recording);
|
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<void> 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].
|
/// Get information on all recordings of the work with ID [workId].
|
||||||
///
|
///
|
||||||
/// This will return a list of [pageSize] recordings. You can get the other
|
/// This will return a list of [pageSize] recordings. You can get the other
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,20 @@
|
||||||
CREATE TABLE persons (
|
CREATE TABLE persons (
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
first_name TEXT NOT NULL,
|
first_name TEXT NOT NULL,
|
||||||
last_name TEXT NOT NULL,
|
last_name TEXT NOT NULL
|
||||||
sync BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
synced BOOLEAN NOT NULL DEFAULT FALSE
|
|
||||||
);
|
);
|
||||||
|
|
||||||
-- This represents real instruments as well as other roles that can be played
|
-- This represents real instruments as well as other roles that can be played
|
||||||
-- in a recording.
|
-- in a recording.
|
||||||
CREATE TABLE instruments (
|
CREATE TABLE instruments (
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL
|
||||||
sync BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
synced BOOLEAN NOT NULL DEFAULT FALSE
|
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE works (
|
CREATE TABLE works (
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
composer INTEGER REFERENCES persons(id) ON DELETE SET NULL,
|
composer INTEGER REFERENCES persons(id) ON DELETE SET NULL,
|
||||||
title TEXT NOT NULL,
|
title TEXT NOT NULL
|
||||||
sync BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
synced BOOLEAN NOT NULL DEFAULT FALSE
|
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE instrumentations (
|
CREATE TABLE instrumentations (
|
||||||
|
|
@ -50,17 +44,13 @@ CREATE TABLE work_sections (
|
||||||
|
|
||||||
CREATE TABLE ensembles (
|
CREATE TABLE ensembles (
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL
|
||||||
sync BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
synced BOOLEAN NOT NULL DEFAULT FALSE
|
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE recordings (
|
CREATE TABLE recordings (
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
work INTEGER REFERENCES works(id) ON DELETE SET NULL,
|
work INTEGER REFERENCES works(id) ON DELETE SET NULL,
|
||||||
comment TEXT NOT NULL,
|
comment TEXT NOT NULL
|
||||||
sync BOOLEAN NOT NULL DEFAULT FALSE,
|
|
||||||
synced BOOLEAN NOT NULL DEFAULT FALSE
|
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE performances (
|
CREATE TABLE performances (
|
||||||
|
|
@ -74,12 +64,6 @@ allPersons:
|
||||||
SELECT * FROM persons ORDER BY last_name, first_name
|
SELECT * FROM persons ORDER BY last_name, first_name
|
||||||
LIMIT :limit OFFSET :offset;
|
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:
|
searchPersons:
|
||||||
SELECT * FROM persons WHERE last_name LIKE :search
|
SELECT * FROM persons WHERE last_name LIKE :search
|
||||||
ORDER BY last_name, first_name LIMIT :limit OFFSET :offset;
|
ORDER BY last_name, first_name LIMIT :limit OFFSET :offset;
|
||||||
|
|
@ -90,12 +74,6 @@ SELECT * FROM persons WHERE id = :id LIMIT 1;
|
||||||
allInstruments:
|
allInstruments:
|
||||||
SELECT * FROM instruments ORDER BY name LIMIT :limit OFFSET :offset;
|
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:
|
searchInstruments:
|
||||||
SELECT * FROM instruments WHERE name LIKE :search ORDER BY name
|
SELECT * FROM instruments WHERE name LIKE :search ORDER BY name
|
||||||
LIMIT :limit OFFSET :offset;
|
LIMIT :limit OFFSET :offset;
|
||||||
|
|
@ -103,12 +81,6 @@ SELECT * FROM instruments WHERE name LIKE :search ORDER BY name
|
||||||
instrumentById:
|
instrumentById:
|
||||||
SELECT * FROM instruments WHERE id = :id LIMIT 1;
|
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:
|
workById:
|
||||||
SELECT * FROM works WHERE id = :id LIMIT 1;
|
SELECT * FROM works WHERE id = :id LIMIT 1;
|
||||||
|
|
||||||
|
|
@ -149,12 +121,6 @@ SELECT instruments.* FROM part_instrumentations
|
||||||
allEnsembles:
|
allEnsembles:
|
||||||
SELECT * FROM ensembles ORDER BY name LIMIT :limit OFFSET :offset;
|
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:
|
searchEnsembles:
|
||||||
SELECT * FROM ensembles WHERE name LIKE :search ORDER BY name
|
SELECT * FROM ensembles WHERE name LIKE :search ORDER BY name
|
||||||
LIMIT :limit OFFSET :offset;
|
LIMIT :limit OFFSET :offset;
|
||||||
|
|
@ -162,12 +128,6 @@ SELECT * FROM ensembles WHERE name LIKE :search ORDER BY name
|
||||||
ensembleById:
|
ensembleById:
|
||||||
SELECT * FROM ensembles WHERE id = :id LIMIT 1;
|
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:
|
recordingById:
|
||||||
SELECT * FROM recordings WHERE id = :id;
|
SELECT * FROM recordings WHERE id = :id;
|
||||||
|
|
||||||
|
|
@ -176,4 +136,4 @@ SELECT * FROM recordings WHERE work = :id ORDER BY id
|
||||||
LIMIT :limit OFFSET :offset;
|
LIMIT :limit OFFSET :offset;
|
||||||
|
|
||||||
performancesByRecording:
|
performancesByRecording:
|
||||||
SELECT * FROM performances WHERE recording = :id;
|
SELECT * FROM performances WHERE recording = :id;
|
||||||
|
|
@ -20,21 +20,6 @@ class PartInfo {
|
||||||
this.instruments,
|
this.instruments,
|
||||||
this.composer,
|
this.composer,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory PartInfo.fromJson(Map<String, dynamic> json) => PartInfo(
|
|
||||||
part: WorkPart.fromJson(json['part']),
|
|
||||||
instruments: json['instruments']
|
|
||||||
.map<Instrument>((j) => Instrument.fromJson(j))
|
|
||||||
.toList(),
|
|
||||||
composer:
|
|
||||||
json['composer'] != null ? Person.fromJson(json['composer']) : null,
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
|
||||||
'part': part.toJson(),
|
|
||||||
'instruments': instruments.map((i) => i.toJson()).toList(),
|
|
||||||
'composers': composer?.toJson(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A bundle information on a work.
|
/// A bundle information on a work.
|
||||||
|
|
@ -66,45 +51,6 @@ class WorkInfo {
|
||||||
this.parts,
|
this.parts,
|
||||||
this.sections,
|
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<String, dynamic> json, {bool sync = false}) =>
|
|
||||||
WorkInfo(
|
|
||||||
work: Work.fromJson(json['work']).copyWith(
|
|
||||||
sync: sync,
|
|
||||||
synced: sync,
|
|
||||||
),
|
|
||||||
instruments: json['instruments']
|
|
||||||
.map<Instrument>((j) => Instrument.fromJson(j).copyWith(
|
|
||||||
sync: sync,
|
|
||||||
synced: sync,
|
|
||||||
))
|
|
||||||
.toList(),
|
|
||||||
composers: json['composers']
|
|
||||||
.map<Person>((j) => Person.fromJson(j).copyWith(
|
|
||||||
sync: sync,
|
|
||||||
synced: sync,
|
|
||||||
))
|
|
||||||
.toList(),
|
|
||||||
parts:
|
|
||||||
json['parts'].map<PartInfo>((j) => PartInfo.fromJson(j)).toList(),
|
|
||||||
sections: json['sections']
|
|
||||||
.map<WorkSection>((j) => WorkSection.fromJson(j))
|
|
||||||
.toList(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> 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.
|
/// All available information on a performance within a recording.
|
||||||
|
|
@ -127,35 +73,6 @@ class PerformanceInfo {
|
||||||
this.ensemble,
|
this.ensemble,
|
||||||
this.role,
|
this.role,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory PerformanceInfo.fromJson(Map<String, dynamic> 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<String, dynamic> toJson() => {
|
|
||||||
'person': person?.toJson(),
|
|
||||||
'ensemble': ensemble?.toJson(),
|
|
||||||
'role': role?.toJson(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// All available information on a recording.
|
/// All available information on a recording.
|
||||||
|
|
@ -173,28 +90,4 @@ class RecordingInfo {
|
||||||
this.recording,
|
this.recording,
|
||||||
this.performances,
|
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<String, dynamic> json,
|
|
||||||
{bool sync = false}) =>
|
|
||||||
RecordingInfo(
|
|
||||||
recording: Recording.fromJson(json['recording']).copyWith(
|
|
||||||
sync: sync,
|
|
||||||
synced: sync,
|
|
||||||
),
|
|
||||||
performances: json['performances']
|
|
||||||
.map<PerformanceInfo>(
|
|
||||||
(j) => PerformanceInfo.fromJson(j, sync: sync))
|
|
||||||
.toList(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
|
||||||
'recording': recording.toJson(),
|
|
||||||
'performances': performances.map((p) => p.toJson()).toList(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,8 @@ environment:
|
||||||
sdk: ">=2.6.0 <3.0.0"
|
sdk: ">=2.6.0 <3.0.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
http:
|
drift: ^1.0.0
|
||||||
meta:
|
|
||||||
moor:
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner:
|
build_runner: ^2.1.10
|
||||||
moor_generator:
|
drift_dev: ^1.0.0
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue