Skip to content

Commit

Permalink
feat: Split into OrganizationalController (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
Gustl22 committed Aug 21, 2024
1 parent df8d086 commit 3d4a186
Show file tree
Hide file tree
Showing 16 changed files with 136 additions and 108 deletions.
5 changes: 3 additions & 2 deletions wrestling_scoreboard_common/lib/src/data/data_object.dart
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ abstract class DataObject {
}
}

abstract class Organizational {
abstract class Organizational extends DataObject {
String? get orgSyncId => null;

Organization? get organization => null;
Expand All @@ -124,5 +124,6 @@ abstract class Organizational {
class DataUnimplementedError extends UnimplementedError {
DataUnimplementedError(CRUD operationType, Type type, [DataObject? filterObject])
: super(
'Data ${operationType.toString().substring(5).toUpperCase()}-request for "${type.toString()}" ${filterObject == null ? '' : 'in "${filterObject.runtimeType.toString()}"'} not found.');
'Data ${operationType.toString().substring(5).toUpperCase()}-request for "${type.toString()}" ${filterObject ==
null ? '' : 'in "${filterObject.runtimeType.toString()}"'} not found.');
}
2 changes: 1 addition & 1 deletion wrestling_scoreboard_common/lib/src/services/api.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import '../../common.dart';
import 'apis/germany_by.dart';

typedef GetSingleOfOrg = Future<T> Function<T extends DataObject>(String orgSyncId, {required int orgId});
typedef GetSingleOfOrg = Future<T> Function<T extends Organizational>(String orgSyncId, {required int orgId});

enum WrestlingApiProvider {
deNwRingenApi,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class ByGermanyWrestlingApi extends WrestlingApi {
@override
GetSingleOfOrg getSingleOfOrg;

late Future<T> Function<T extends DataObject>(String orgSyncId) _getSingleBySyncId;
late Future<T> Function<T extends Organizational>(String orgSyncId) _getSingleBySyncId;

ByGermanyWrestlingApi(
this.organization, {
Expand All @@ -40,7 +40,7 @@ class ByGermanyWrestlingApi extends WrestlingApi {
this.authService,
}) {
_getSingleBySyncId =
<T extends DataObject>(String orgSyncId) => getSingleOfOrg<T>(orgSyncId, orgId: organization.id!);
<T extends Organizational>(String orgSyncId) => getSingleOfOrg<T>(orgSyncId, orgId: organization.id!);
}

@override
Expand Down
2 changes: 1 addition & 1 deletion wrestling_scoreboard_common/test/services/apis_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ void main() {
wrestlingApi = WrestlingApiProvider.deNwRingenApi.getApi(
organizationNRW,
authService: BasicAuthService(username: '', password: ''),
getSingleOfOrg: <T extends DataObject>(String orgSyncId, {required int orgId}) async {
getSingleOfOrg: <T extends Organizational>(String orgSyncId, {required int orgId}) async {
switch (T) {
case const (Club):
return (await wrestlingApi.importClubs()).singleWhere((club) => club.orgSyncId == orgSyncId) as T;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import 'package:shelf/shelf.dart';
import 'package:wrestling_scoreboard_common/common.dart';
import 'package:wrestling_scoreboard_server/controllers/entity_controller.dart';
import 'package:wrestling_scoreboard_server/controllers/membership_controller.dart';
import 'package:wrestling_scoreboard_server/controllers/organizational_controller.dart';
import 'package:wrestling_scoreboard_server/controllers/team_controller.dart';
import 'package:wrestling_scoreboard_server/request.dart';

class ClubController extends EntityController<Club> {
class ClubController extends OrganizationalController<Club> {
static final ClubController _singleton = ClubController._internal();

factory ClubController() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,8 @@ class DatabaseController {

Future<void> _restore(String dumpPath) async {
final db = PostgresDb();
final conn = db.connection;
{
await conn.execute('DROP SCHEMA IF EXISTS public CASCADE;');
await db.connection.execute('DROP SCHEMA IF EXISTS public CASCADE;');
await db.close();
}

Expand All @@ -97,6 +96,9 @@ class DatabaseController {
final processResult = await Process.run('psql', args, environment: {'PGPASSWORD': db.dbPW});
await db.open();

Iterable<EntityController> entityControllers = dataTypes.map((t) => EntityController.getControllerFromDataType(t));
await Future.forEach(entityControllers, (e) => e.init());

if (processResult.exitCode != 0) {
throw processResult.stderr;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import 'package:wrestling_scoreboard_common/common.dart';
import 'package:wrestling_scoreboard_server/controllers/division_weight_class_controller.dart';
import 'package:wrestling_scoreboard_server/controllers/entity_controller.dart';
import 'package:wrestling_scoreboard_server/controllers/league_controller.dart';
import 'package:wrestling_scoreboard_server/controllers/organizational_controller.dart';
import 'package:wrestling_scoreboard_server/controllers/weight_class_controller.dart';
import 'package:wrestling_scoreboard_server/request.dart';

class DivisionController extends EntityController<Division> {
class DivisionController extends OrganizationalController<Division> {
static final DivisionController _singleton = DivisionController._internal();

factory DivisionController() {
Expand Down
73 changes: 16 additions & 57 deletions wrestling_scoreboard_server/lib/controllers/entity_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,20 @@ abstract class EntityController<T extends DataObject> {
String tableName;
String primaryKeyName;

EntityController({required this.tableName, this.primaryKeyName = 'id'});
late Future<psql.Statement> getSingleRawStmt;
late Future<psql.Statement> deleteSingleStmt;

EntityController({required this.tableName, this.primaryKeyName = 'id'}) {
init();
}

void init() {
getSingleRawStmt =
PostgresDb().connection.prepare(psql.Sql.named('SELECT * FROM $tableName WHERE $primaryKeyName = @id;'));

deleteSingleStmt =
PostgresDb().connection.prepare(psql.Sql.named('DELETE FROM $tableName WHERE $primaryKeyName = @id;'));
}

Future<Response> postSingle(Request request) async {
final message = await request.readAsString();
Expand All @@ -80,37 +93,13 @@ abstract class EntityController<T extends DataObject> {
return DataObject.fromRaw<T>(single, getSingleFromDataType);
}

/// Get a single data object via a foreign id (sync id), given by an organization.
Future<T> getSingleOfOrg(String orgSyncId, {required int orgId}) async {
final single = await getSingleOfOrgRaw(orgSyncId, orgId: orgId);
return DataObject.fromRaw<T>(single, getSingleFromDataType);
}

late final getSingleRawStmt =
PostgresDb().connection.prepare(psql.Sql.named('SELECT * FROM $tableName WHERE $primaryKeyName = @id;'));

Future<Map<String, dynamic>> getSingleRaw(int id) async {
final resStream = (await getSingleRawStmt).bind({'id': id});
final many = await resStream.toColumnMap().toList();
if (many.isEmpty) throw InvalidParameterException('$T with id "$id" not found');
return many.first;
}

late final getSingleOfOrgRawStmt = PostgresDb()
.connection
.prepare(psql.Sql.named('SELECT * FROM $tableName WHERE organization_id = @orgId AND org_sync_id = @orgSyncId;'));

Future<Map<String, dynamic>> getSingleOfOrgRaw(String orgSyncId, {required int orgId}) async {
if (orgSyncId != orgSyncId.trim()) {
orgSyncId = orgSyncId.trim();
print('$T with orgSyncId "$orgSyncId" was trimmed');
}
final resStream = (await getSingleOfOrgRawStmt).bind({'orgSyncId': orgSyncId, 'orgId': orgId});
final many = await resStream.toColumnMap().toList();
if (many.isEmpty) throw InvalidParameterException('$T with orgSyncId "$orgSyncId" not found');
return many.first;
}

Future<int> createSingle(T dataObject) async {
return await createSingleRaw(dataObject.toRaw());
}
Expand Down Expand Up @@ -140,25 +129,6 @@ abstract class EntityController<T extends DataObject> {
return dataObject.copyWithId(await createSingle(dataObject)) as T;
}

Future<T> getOrCreateSingleOfOrg(T dataObject) async {
if (dataObject.id != null) {
throw Exception('Data object already has an id: $dataObject');
}
if (dataObject is! Organizational) {
throw Exception('Data object is not Organizational: $dataObject');
}
final organizational = (dataObject as Organizational);
if (organizational.organization?.id == null || organizational.orgSyncId == null) {
throw Exception('Organization id and sync id must not be null: $dataObject');
}
try {
final single = await getSingleOfOrg(organizational.orgSyncId!, orgId: organizational.organization!.id!);
return single;
} on InvalidParameterException catch (_) {
return createSingleReturn(dataObject);
}
}

Future<List<int>> createMany(List<T> dataObjects) async {
return await Future.wait(dataObjects.map((element) => createSingle(element)));
}
Expand All @@ -167,10 +137,6 @@ abstract class EntityController<T extends DataObject> {
return await Future.wait(dataObjects.map((element) => createSingleReturn(element)));
}

Future<List<T>> getOrCreateManyOfOrg(List<T> dataObjects) async {
return await Future.wait(dataObjects.map((element) => getOrCreateSingleOfOrg(element)));
}

Future<int> updateSingle(T dataObject) async {
return updateSingleRaw(dataObject.toRaw());
}
Expand Down Expand Up @@ -211,9 +177,6 @@ abstract class EntityController<T extends DataObject> {
});
}

late final deleteSingleStmt =
PostgresDb().connection.prepare(psql.Sql.named('DELETE FROM $tableName WHERE $primaryKeyName = @id;'));

Future<bool> deleteSingle(int id) async {
try {
await (await deleteSingleStmt).bind({'id': id}).toList();
Expand Down Expand Up @@ -435,10 +398,6 @@ abstract class EntityController<T extends DataObject> {
return getControllerFromDataType(T).getSingle(id) as Future<T>;
}

static Future<T> getSingleFromDataTypeOfOrg<T extends DataObject>(String orgSyncId, {required int orgId}) {
return getControllerFromDataType(T).getSingleOfOrg(orgSyncId, orgId: orgId) as Future<T>;
}

static Future<List<T>> getManyFromDataType<T extends DataObject>(
{List<String>? conditions, Conjunction conjunction = Conjunction.and, Map<String, dynamic>? substitutionValues}) {
return getControllerFromDataType(T).getMany(
Expand Down Expand Up @@ -507,15 +466,15 @@ class InvalidParameterException implements Exception {
}
}

extension on Map<String, dynamic> {
extension ToPsqlParser on Map<String, dynamic> {
/// Parse custom postgres types
/// https://github.com/isoos/postgresql-dart/issues/276
Map<String, dynamic> parse() {
return map((key, value) => MapEntry(key, value is psql.UndecodedBytes ? value.asString : value));
}
}

extension on psql.ResultStream {
extension FromPsqlParser on psql.ResultStream {
/// Parse custom postgres types
/// https://github.com/isoos/postgresql-dart/issues/276
// ignore: unused_element
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:wrestling_scoreboard_server/controllers/league_team_participatio
import 'package:wrestling_scoreboard_server/controllers/lineup_controller.dart';
import 'package:wrestling_scoreboard_server/controllers/membership_controller.dart';
import 'package:wrestling_scoreboard_server/controllers/organization_controller.dart';
import 'package:wrestling_scoreboard_server/controllers/organizational_controller.dart';
import 'package:wrestling_scoreboard_server/controllers/participant_state_controller.dart';
import 'package:wrestling_scoreboard_server/controllers/participation_controller.dart';
import 'package:wrestling_scoreboard_server/controllers/person_controller.dart';
Expand All @@ -16,7 +17,7 @@ import 'package:wrestling_scoreboard_server/request.dart';

import 'bout_controller.dart';

class LeagueController extends EntityController<League> {
class LeagueController extends OrganizationalController<League> {
static final LeagueController _singleton = LeagueController._internal();

factory LeagueController() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import 'package:wrestling_scoreboard_common/common.dart';
import 'package:wrestling_scoreboard_server/controllers/organizational_controller.dart';

import 'entity_controller.dart';

class MembershipController extends EntityController<Membership> {
class MembershipController extends OrganizationalController<Membership> {
static final MembershipController _singleton = MembershipController._internal();

factory MembershipController() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:wrestling_scoreboard_server/controllers/competition_controller.d
import 'package:wrestling_scoreboard_server/controllers/division_controller.dart';
import 'package:wrestling_scoreboard_server/controllers/division_weight_class_controller.dart';
import 'package:wrestling_scoreboard_server/controllers/entity_controller.dart';
import 'package:wrestling_scoreboard_server/controllers/organizational_controller.dart';
import 'package:wrestling_scoreboard_server/controllers/team_controller.dart';
import 'package:wrestling_scoreboard_server/controllers/weight_class_controller.dart';
import 'package:wrestling_scoreboard_server/request.dart';
Expand Down Expand Up @@ -55,7 +56,7 @@ class OrganizationController extends EntityController<Organization> {
}

final organization = await getSingle(organizationId);
return organization.getApi(EntityController.getSingleFromDataTypeOfOrg, authService: authService);
return organization.getApi(OrganizationalController.getSingleFromDataTypeOfOrg, authService: authService);
}

Future<Response> import(Request request, String organizationId) async {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import 'package:postgres/postgres.dart' as psql;
import 'package:wrestling_scoreboard_common/common.dart';
import 'package:wrestling_scoreboard_server/controllers/entity_controller.dart';
import 'package:wrestling_scoreboard_server/services/postgres_db.dart';

abstract class OrganizationalController<T extends Organizational> extends EntityController<T> {
OrganizationalController({required super.tableName});

late Future<psql.Statement> getSingleOfOrgRawStmt;

@override
void init() {
getSingleOfOrgRawStmt = PostgresDb().connection.prepare(
psql.Sql.named('SELECT * FROM $tableName WHERE organization_id = @orgId AND org_sync_id = @orgSyncId;'));
super.init();
}

/// Get a single data object via a foreign id (sync id), given by an organization.
Future<T> getSingleOfOrg(String orgSyncId, {required int orgId}) async {
final single = await getSingleOfOrgRaw(orgSyncId, orgId: orgId);
return DataObject.fromRaw<T>(single, EntityController.getSingleFromDataType);
}

Future<Map<String, dynamic>> getSingleOfOrgRaw(String orgSyncId, {required int orgId}) async {
if (orgSyncId != orgSyncId.trim()) {
orgSyncId = orgSyncId.trim();
print('$T with orgSyncId "$orgSyncId" was trimmed');
}
final resStream = (await getSingleOfOrgRawStmt).bind({'orgSyncId': orgSyncId, 'orgId': orgId});
final many = await resStream.toColumnMap().toList();
if (many.isEmpty) throw InvalidParameterException('$T with orgSyncId "$orgSyncId" not found');
return many.first;
}

Future<T> getOrCreateSingleOfOrg(T dataObject) async {
if (dataObject.id != null) {
throw Exception('Data object already has an id: $dataObject');
}
final organizational = (dataObject as Organizational);
if (organizational.organization?.id == null || organizational.orgSyncId == null) {
throw Exception('Organization id and sync id must not be null: $dataObject');
}
try {
final single = await getSingleOfOrg(organizational.orgSyncId!, orgId: organizational.organization!.id!);
return single;
} on InvalidParameterException catch (_) {
return createSingleReturn(dataObject);
}
}

Future<List<T>> getOrCreateManyOfOrg(List<T> dataObjects) async {
return await Future.wait(dataObjects.map((element) => getOrCreateSingleOfOrg(element)));
}

static Future<T> getSingleFromDataTypeOfOrg<T extends Organizational>(String orgSyncId, {required int orgId}) {
return (EntityController.getControllerFromDataType(T) as OrganizationalController<T>)
.getSingleOfOrg(orgSyncId, orgId: orgId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import 'package:postgres/postgres.dart' as psql;
import 'package:shelf/shelf.dart';
import 'package:wrestling_scoreboard_common/common.dart';
import 'package:wrestling_scoreboard_server/controllers/membership_controller.dart';
import 'package:wrestling_scoreboard_server/controllers/organizational_controller.dart';
import 'package:wrestling_scoreboard_server/request.dart';

import 'entity_controller.dart';

class PersonController extends EntityController<Person> {
class PersonController extends OrganizationalController<Person> {
static final PersonController _singleton = PersonController._internal();

factory PersonController() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import 'package:shelf/shelf.dart';
import 'package:wrestling_scoreboard_common/common.dart';
import 'package:wrestling_scoreboard_server/controllers/organizational_controller.dart';
import 'package:wrestling_scoreboard_server/controllers/team_match_controller.dart';
import 'package:wrestling_scoreboard_server/request.dart';

import 'entity_controller.dart';

class TeamController extends EntityController<Team> {
class TeamController extends OrganizationalController<Team> {
static final TeamController _singleton = TeamController._internal();

factory TeamController() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:postgres/postgres.dart' as psql;
import 'package:shelf/shelf.dart';
import 'package:wrestling_scoreboard_common/common.dart';
import 'package:wrestling_scoreboard_server/controllers/division_controller.dart';
import 'package:wrestling_scoreboard_server/controllers/organizational_controller.dart';
import 'package:wrestling_scoreboard_server/controllers/participant_state_controller.dart';
import 'package:wrestling_scoreboard_server/controllers/participation_controller.dart';
import 'package:wrestling_scoreboard_server/controllers/team_match_bout_controller.dart';
Expand All @@ -14,7 +15,7 @@ import 'package:wrestling_scoreboard_server/services/postgres_db.dart';
import 'bout_controller.dart';
import 'entity_controller.dart';

class TeamMatchController extends EntityController<TeamMatch> {
class TeamMatchController extends OrganizationalController<TeamMatch> {
static final TeamMatchController _singleton = TeamMatchController._internal();

factory TeamMatchController() {
Expand Down
Loading

0 comments on commit 3d4a186

Please sign in to comment.