Skip to content

Commit

Permalink
feat: rework deserialization of unsupported types to throw exception
Browse files Browse the repository at this point in the history
  • Loading branch information
hig-dev committed Feb 6, 2024
1 parent f7a2d50 commit 54956cf
Show file tree
Hide file tree
Showing 33 changed files with 108 additions and 246 deletions.
4 changes: 2 additions & 2 deletions app/lib/screens/app_onboarding/preview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ class Preview {
if (selectedStudyObjectId != null) {
try {
if (selectedRoute == '/intervention') {
final List<StudySubject> studySubjects = await SupabaseQuery.getAll<StudySubject>(
final studySubjects = await SupabaseQuery.getAll<StudySubject>(
selectedColumns: [
'*',
'study!study_subject_studyId_fkey(*)',
Expand All @@ -130,7 +130,7 @@ class Preview {
// If the user has a study object Id, there was already a subject created
// and we need to find the last one they created for the study
// with the correct interventions
subject = studySubjects.lastWhere(
subject = studySubjects.extracted.lastWhere(
(foundSubject) {
// todo baseline
foundSubject.study.schedule.includeBaseline = false;
Expand Down
35 changes: 20 additions & 15 deletions app/lib/screens/study/onboarding/study_selection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -91,23 +91,22 @@ class StudySelectionScreen extends StatelessWidget {
),
),
Expanded(
child: RetryFutureBuilder<List<Study>>(
child: RetryFutureBuilder<ExtractedSupabaseListResult<Study>>(
tryFunction: () async => Study.publishedPublicStudies(),
successBuilder: (BuildContext context, List<Study>? studies) {
successBuilder: (BuildContext context, ExtractedSupabaseListResult<Study>? studies) {
if (studies!.notExtracted.isNotEmpty) {
debugPrint('${studies.notExtracted.length} studies could not be extracted.');
}
return ListView.builder(
itemCount: studies!.length,
itemCount: studies.extracted.length,
itemBuilder: (context, index) {
final study = studies[index];
final study = studies.extracted[index];
return Hero(
tag: 'study_tile_${studies[index].id}',
tag: 'study_tile_${studies.extracted[index].id}',
child: Material(
child: StudyTile.fromStudy(
study: study,
onTap: () async {
if (!study.isSupported) {
await showAppOutdatedDialog(context);
return;
}
await navigateToStudyOverview(context, study);
},
),
Expand Down Expand Up @@ -209,16 +208,22 @@ class _InviteCodeDialogState extends State<InviteCodeDialog> {
}

if (studyResult != null) {
final study = Study.fromJson(studyResult);

if (!mounted) return;
Navigator.pop(context);

if (!study.isSupported) {
Study study;
try {
study = Study.fromJson(studyResult);
// ignore: avoid_catching_errors
} on ArgumentError catch (error) {
// We are catching ArgumentError because unknown enums throw an ArgumentError
// and UnknownJsonTypeError is a subclass of ArgumentError
debugPrint('Study selection from invite failed: $error');
if (!mounted) return;
await showAppOutdatedDialog(context);
return;
}

if (!mounted) return;
Navigator.pop(context);

if (result.containsKey('preselected_intervention_ids') &&
result['preselected_intervention_ids'] != null) {
final preselectedIds = List<String>.from(
Expand Down
8 changes: 4 additions & 4 deletions app/lib/screens/study/report/report_history.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ class ReportHistoryScreen extends StatelessWidget {
AppLocalizations.of(context)!.report_history,
),
),
body: RetryFutureBuilder<List<StudySubject>>(
body: RetryFutureBuilder<ExtractedSupabaseListResult<StudySubject>>(
tryFunction: () => StudySubject.getStudyHistory(Supabase.instance.client.auth.currentUser!.id),
successBuilder: (BuildContext context, List<StudySubject>? pastStudies) {
successBuilder: (BuildContext context, ExtractedSupabaseListResult<StudySubject>? pastStudies) {
return ListView.builder(
itemCount: pastStudies!.length,
itemCount: pastStudies!.extracted.length,
itemBuilder: (context, index) {
return ReportHistoryItem(pastStudies[index]);
return ReportHistoryItem(pastStudies.extracted[index]);
},
);
},
Expand Down
1 change: 0 additions & 1 deletion core/lib/src/models/eligibility/eligibility_criterion.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,4 @@ class EligibilityCriterion {

bool isSatisfied(QuestionnaireState qs) => condition.evaluate(qs) == true;
bool isViolated(QuestionnaireState qs) => condition.evaluate(qs) == false;
bool get isSupported => condition.isSupported;
}
4 changes: 2 additions & 2 deletions core/lib/src/models/expressions/expression.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:studyu_core/src/models/expressions/types/types.dart';
import 'package:studyu_core/src/models/expressions/types/unknown_expression.dart';
import 'package:studyu_core/src/models/questionnaire/questionnaire_state.dart';
import 'package:studyu_core/src/models/unknown_json_type_error.dart';

typedef ExpressionParser = Expression Function(Map<String, dynamic> data);

Expand All @@ -16,7 +16,7 @@ abstract class Expression {
BooleanExpression.expressionType => BooleanExpression.fromJson(data),
ChoiceExpression.expressionType => ChoiceExpression.fromJson(data),
NotExpression.expressionType => NotExpression.fromJson(data),
_ => UnknownExpression()
_ => throw UnknownJsonTypeError(data[keyType])
};

Map<String, dynamic> toJson();
Expand Down
19 changes: 0 additions & 19 deletions core/lib/src/models/expressions/types/unknown_expression.dart

This file was deleted.

2 changes: 0 additions & 2 deletions core/lib/src/models/interventions/intervention.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ class Intervention {

Intervention.withId() : id = const Uuid().v4();

bool get isSupported => tasks.every((task) => task.isSupported);

factory Intervention.fromJson(Map<String, dynamic> data) => _$InterventionFromJson(data);

Map<String, dynamic> toJson() => _$InterventionToJson(this);
Expand Down
4 changes: 2 additions & 2 deletions core/lib/src/models/interventions/intervention_task.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:studyu_core/src/models/interventions/tasks/checkmark_task.dart';
import 'package:studyu_core/src/models/interventions/tasks/unknown_intervention_task.dart';
import 'package:studyu_core/src/models/tasks/task.dart';
import 'package:studyu_core/src/models/unknown_json_type_error.dart';

typedef InterventionTaskParser = InterventionTask Function(Map<String, dynamic> data);

Expand All @@ -13,6 +13,6 @@ abstract class InterventionTask extends Task {

factory InterventionTask.fromJson(Map<String, dynamic> data) => switch (data[Task.keyType]) {
CheckmarkTask.taskType => CheckmarkTask.fromJson(data),
_ => UnknownInterventionTask(),
_ => throw UnknownJsonTypeError(data[Task.keyType]),
};
}

This file was deleted.

1 change: 1 addition & 0 deletions core/lib/src/models/models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ export 'tables/study_subject.dart';
export 'tables/subject_progress.dart';
export 'tables/user.dart';
export 'tasks/tasks.dart';
export 'unknown_json_type_error.dart';
6 changes: 2 additions & 4 deletions core/lib/src/models/observations/observation.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:studyu_core/src/models/observations/tasks/tasks.dart';
import 'package:studyu_core/src/models/observations/tasks/unknown_task.dart';
import 'package:studyu_core/src/models/tasks/task.dart';
import 'package:studyu_core/src/models/unknown_json_type_error.dart';

typedef ObservationTaskParser = Observation Function(Map<String, dynamic> data);

Expand All @@ -9,10 +9,8 @@ abstract class Observation extends Task {

Observation.withId(super.type) : super.withId();

bool get isSupported;

factory Observation.fromJson(Map<String, dynamic> data) => switch (data[Task.keyType]) {
QuestionnaireTask.taskType => QuestionnaireTask.fromJson(data),
_ => UnknownTask(),
_ => throw UnknownJsonTypeError(data[Task.keyType]),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ class QuestionnaireTask extends Observation {
@override
Map<String, dynamic> toJson() => _$QuestionnaireTaskToJson(this);

@override
bool get isSupported => questions.isSupported;

@override
Map<DateTime, T> extractPropertyResults<T>(String property, List<SubjectProgress> sourceResults) {
final Question? targetQuestion = questions.questions.firstWhereOrNull((q) => q.id == property);
Expand Down
30 changes: 0 additions & 30 deletions core/lib/src/models/observations/tasks/unknown_task.dart

This file was deleted.

5 changes: 2 additions & 3 deletions core/lib/src/models/questionnaire/question.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@ import 'package:studyu_core/src/models/questionnaire/answer.dart';
import 'package:studyu_core/src/models/questionnaire/question_conditional.dart';
import 'package:studyu_core/src/models/questionnaire/questionnaire_state.dart';
import 'package:studyu_core/src/models/questionnaire/questions/questions.dart';
import 'package:studyu_core/src/models/questionnaire/questions/unknown_question.dart';
import 'package:studyu_core/src/models/unknown_json_type_error.dart';
import 'package:uuid/uuid.dart';

typedef QuestionParser = Question Function(Map<String, dynamic> data);

abstract class Question<V> {
static const String keyType = 'type';
String type;
bool get isSupported => true;
late String id;
String? prompt;
String? rationale;
Expand All @@ -29,7 +28,7 @@ abstract class Question<V> {
AnnotatedScaleQuestion.questionType => AnnotatedScaleQuestion.fromJson(data),
VisualAnalogueQuestion.questionType => VisualAnalogueQuestion.fromJson(data),
FreeTextQuestion.questionType => FreeTextQuestion.fromJson(data),
_ => UnknownQuestion(),
_ => throw UnknownJsonTypeError(data[keyType]),
} as Question<V>;

Map<String, dynamic> toJson();
Expand Down
2 changes: 0 additions & 2 deletions core/lib/src/models/questionnaire/questionnaire.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ class StudyUQuestionnaire {

StudyUQuestionnaire();

bool get isSupported => questions.every((question) => question.isSupported);

factory StudyUQuestionnaire.fromJson(List<dynamic> data) =>
StudyUQuestionnaire()..questions = data.map((entry) => Question.fromJson(entry as Map<String, dynamic>)).toList();

Expand Down
14 changes: 0 additions & 14 deletions core/lib/src/models/questionnaire/questions/unknown_question.dart

This file was deleted.

6 changes: 2 additions & 4 deletions core/lib/src/models/report/report_section.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:studyu_core/src/models/report/sections/average_section.dart';
import 'package:studyu_core/src/models/report/sections/linear_regression_section.dart';
import 'package:studyu_core/src/models/report/sections/unknown_section.dart';
import 'package:studyu_core/src/models/unknown_json_type_error.dart';
import 'package:uuid/uuid.dart';

typedef SectionParser = ReportSection Function(Map<String, dynamic> data);
Expand All @@ -12,16 +12,14 @@ abstract class ReportSection {
String? title;
String? description;

bool get isSupported => true;

ReportSection(this.type);

ReportSection.withId(this.type) : id = const Uuid().v4();

factory ReportSection.fromJson(Map<String, dynamic> data) => switch (data[keyType]) {
AverageSection.sectionType => AverageSection.fromJson(data),
LinearRegressionSection.sectionType => LinearRegressionSection.fromJson(data),
_ => UnknownSection(),
_ => throw UnknownJsonTypeError(data[keyType]),
};
Map<String, dynamic> toJson();

Expand Down
2 changes: 0 additions & 2 deletions core/lib/src/models/report/report_specification.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ class ReportSpecification {

ReportSpecification();

bool get isSupported => (primary == null || primary!.isSupported) && secondary.every((r) => r.isSupported);

factory ReportSpecification.fromJson(Map<String, dynamic> json) => _$ReportSpecificationFromJson(json);
Map<String, dynamic> toJson() => _$ReportSpecificationToJson(this);

Expand Down
15 changes: 0 additions & 15 deletions core/lib/src/models/report/sections/unknown_section.dart

This file was deleted.

6 changes: 2 additions & 4 deletions core/lib/src/models/results/result.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'package:collection/collection.dart';
import 'package:json_annotation/json_annotation.dart';

import 'package:studyu_core/src/models/questionnaire/questionnaire_state.dart';
import 'package:studyu_core/src/models/unknown_json_type_error.dart';

part 'result.g.dart';

Expand All @@ -14,8 +14,6 @@ class Result<T> {

static const String keyResult = 'result';

bool get isSupported => type != 'unknown';

@JsonKey(includeToJson: false, includeFromJson: false)
late T result;

Expand All @@ -29,7 +27,7 @@ class Result<T> {
'QuestionnaireState' => Result<QuestionnaireState>.parseJson(json)
..result = QuestionnaireState.fromJson(List<Map<String, dynamic>>.from(json[keyResult] as List)),
'bool' => Result<bool>.parseJson(json)..result = json[keyResult] as bool,
_ => Result<bool>("unknown")..result = false,
_ => throw UnknownJsonTypeError(json[keyType]),
} as Result<T>;

Map<String, dynamic> toJson() {
Expand Down
Loading

0 comments on commit 54956cf

Please sign in to comment.