From 8c281fafb44cc19169e008b163266fa62a6f5d41 Mon Sep 17 00:00:00 2001 From: MarkG Date: Tue, 29 Jun 2021 10:52:58 +0700 Subject: [PATCH] chore: add fetch survey detail --- lib/configs/factories.dart | 6 ++ lib/gen/configs.gen.dart | 3 + lib/models/auth_token_info.dart | 12 +-- lib/models/detailed_survey_info.dart | 7 ++ lib/models/survey_info.dart | 9 +- lib/models/survey_question_info.dart | 87 +++++++++++++++++++ lib/models/user_info.dart | 4 +- lib/repositories/survey_repository.dart | 9 ++ lib/services/api/api_raw_response.dart | 30 +++++++ lib/services/api/api_service.dart | 16 ++-- .../api/survey/survey_api_service.dart | 24 ++++- pubspec.lock | 6 +- pubspec.yaml | 6 +- 13 files changed, 193 insertions(+), 26 deletions(-) create mode 100644 lib/models/detailed_survey_info.dart create mode 100644 lib/models/survey_question_info.dart create mode 100644 lib/services/api/api_raw_response.dart diff --git a/lib/configs/factories.dart b/lib/configs/factories.dart index 4ad3849b..534a474b 100644 --- a/lib/configs/factories.dart +++ b/lib/configs/factories.dart @@ -4,4 +4,10 @@ final Map _factories = { UserInfo: () => UserInfo(), AuthTokenInfo: () => AuthTokenInfo(), SurveyInfo: () => SurveyInfo(), + ApiRawResponse: () => ApiRawResponse(), + ApiRawObject: () => ApiRawObject(), + DetailedSurveyInfo: () => DetailedSurveyInfo(), + SurveyQuestionInfo: () => SurveyQuestionInfo(), + SurveyQuestionDisplayType: (v) => SurveyQuestionDisplayType(v as String), + SurveyQuestionPickType: (v) => SurveyQuestionPickType(v as String), }; diff --git a/lib/gen/configs.gen.dart b/lib/gen/configs.gen.dart index df6f316b..6b5aa214 100644 --- a/lib/gen/configs.gen.dart +++ b/lib/gen/configs.gen.dart @@ -1,7 +1,9 @@ import 'package:survey/gen/flavors.gen.dart'; import 'package:flutter/widgets.dart'; import 'package:survey/models/auth_token_info.dart'; +import 'package:survey/models/detailed_survey_info.dart'; import 'package:survey/models/survey_info.dart'; +import 'package:survey/models/survey_question_info.dart'; import 'package:survey/models/user_info.dart'; import 'package:survey/modules/forgot_password/forgot_password_module.dart'; import 'package:survey/modules/home/home_module.dart'; @@ -9,6 +11,7 @@ import 'package:survey/modules/landing/landing_module.dart'; import 'package:survey/modules/login/login_module.dart'; import 'package:survey/modules/side_menu/side_menu_module.dart'; import 'package:survey/modules/survey_detail/survey_detail_module.dart'; +import 'package:survey/services/api/api_service.dart'; part 'package:survey/configs/app.dart'; part 'package:survey/configs/factories.dart'; diff --git a/lib/models/auth_token_info.dart b/lib/models/auth_token_info.dart index 3073aa3b..2baeb6f8 100644 --- a/lib/models/auth_token_info.dart +++ b/lib/models/auth_token_info.dart @@ -8,11 +8,13 @@ class AuthTokenInfo with Mappable { @override void mapping(Mapper map) { - map("access_token", accessToken, (v) => accessToken = v as String); - map("token_type", tokenType, (v) => tokenType = v as String); - map("expires_in", expiresIn, (v) => expiresIn = v as DateTime, - DateTransform()); + map("attributes.access_token", accessToken, + (v) => accessToken = v as String); map( - "refresh_token", refreshToken, (v) => refreshToken = v as String); + "attributes.token_type", tokenType, (v) => tokenType = v as String); + map("attributes.expires_in", expiresIn, + (v) => expiresIn = v as DateTime, const DateTransform()); + map("attributes.refresh_token", refreshToken, + (v) => refreshToken = v as String); } } diff --git a/lib/models/detailed_survey_info.dart b/lib/models/detailed_survey_info.dart new file mode 100644 index 00000000..90e5e35b --- /dev/null +++ b/lib/models/detailed_survey_info.dart @@ -0,0 +1,7 @@ +import 'package:survey/models/survey_info.dart'; +import 'package:survey/models/survey_question_info.dart'; + +// ignore: must_be_immutable +class DetailedSurveyInfo extends SurveyInfo { + late List questions; +} diff --git a/lib/models/survey_info.dart b/lib/models/survey_info.dart index 4e1f495f..175772ad 100644 --- a/lib/models/survey_info.dart +++ b/lib/models/survey_info.dart @@ -3,15 +3,18 @@ import 'package:equatable/equatable.dart'; // ignore: must_be_immutable class SurveyInfo extends Equatable with Mappable { + String? id; String? title; String? description; String? coverImageUrl; @override void mapping(Mapper map) { - map("title", title, (v) => title = v as String?); - map("description", description, (v) => description = v as String?); - map("cover_image_url", coverImageUrl, (v) { + map("id", id, (v) => id = v as String?); + map("attributes.title", title, (v) => title = v as String?); + map("attributes.description", description, + (v) => description = v as String?); + map("attributes.cover_image_url", coverImageUrl, (v) { final url = v as String?; coverImageUrl = url != null ? "${url}l" : null; }); diff --git a/lib/models/survey_question_info.dart b/lib/models/survey_question_info.dart new file mode 100644 index 00000000..338b77d0 --- /dev/null +++ b/lib/models/survey_question_info.dart @@ -0,0 +1,87 @@ +import 'package:object_mapper/object_mapper.dart'; + +class SurveyQuestionInfo with Mappable { + String? id; + String? content; + int? displayOrder; + SurveyQuestionPickType? pickType; + SurveyQuestionDisplayType? displayType; + bool? isMandatory; + String? coverImageUrl; + double? coverImageOpacity; + + @override + void mapping(Mapper map) { + map( + "id", + id, + (v) => id = v as String, + ); + map( + "attributes.text", + content, + (v) => content = v as String, + ); + map( + "attributes.display_order", + displayOrder, + (v) => displayOrder = v as int, + ); + map( + "attributes.pick", + pickType, + (v) => pickType = v as SurveyQuestionPickType?, + const EnumTransform(), + ); + map( + "attributes.display_type", + displayType, + (v) => displayType = v as SurveyQuestionDisplayType?, + const EnumTransform(), + ); + map( + "attributes.is_mandatory", + isMandatory, + (v) => isMandatory = v as bool, + ); + map( + "attributes.cover_image_url", + coverImageUrl, + (v) => coverImageUrl = v as String?, + ); + map( + "attributes.cover_image_opacity", + coverImageOpacity, + (v) => coverImageOpacity = v as double?, + ); + } +} + +// ignore: avoid_implementing_value_types +class SurveyQuestionDisplayType extends Enumerable { + const SurveyQuestionDisplayType(this.rawValue); + + @override + final String rawValue; + + static const intro = SurveyQuestionDisplayType("intro"); + static const star = SurveyQuestionDisplayType("star"); + static const heart = SurveyQuestionDisplayType("heart"); + static const smiley = SurveyQuestionDisplayType("smiley"); + static const choice = SurveyQuestionDisplayType("choice"); + static const nps = SurveyQuestionDisplayType("NPS"); + static const textarea = SurveyQuestionDisplayType("textarea"); + static const textField = SurveyQuestionDisplayType("textfield"); + static const outro = SurveyQuestionDisplayType("outro"); +} + +class SurveyQuestionPickType extends Enumerable { + const SurveyQuestionPickType(this.rawValue); + + @override + final String rawValue; + + static const none = SurveyQuestionDisplayType("none"); + static const one = SurveyQuestionDisplayType("one"); + static const any = SurveyQuestionDisplayType("any"); +} diff --git a/lib/models/user_info.dart b/lib/models/user_info.dart index 61f2d443..9b271e79 100644 --- a/lib/models/user_info.dart +++ b/lib/models/user_info.dart @@ -6,7 +6,7 @@ class UserInfo with Mappable { @override void mapping(Mapper map) { - map("email", email, (v) => email = v as String); - map("avatar_url", avatarUrl, (v) => avatarUrl = v as String); + map("attributes.email", email, (v) => email = v as String); + map("attributes.avatar_url", avatarUrl, (v) => avatarUrl = v as String); } } diff --git a/lib/repositories/survey_repository.dart b/lib/repositories/survey_repository.dart index c6b7bb0c..0423d9de 100644 --- a/lib/repositories/survey_repository.dart +++ b/lib/repositories/survey_repository.dart @@ -1,3 +1,4 @@ +import 'package:survey/models/detailed_survey_info.dart'; import 'package:survey/models/survey_info.dart'; import 'package:survey/services/api/survey/survey_api_service.dart'; import 'package:survey/services/local_storage/local_storage_service.dart'; @@ -11,6 +12,8 @@ abstract class SurveyRepository { Future> fetchSurveysFromCached(); Future> fetchSurveysFromRemote(); + + Future fetchDetailedSurvey(String id); } class SurveyRepositoryImpl implements SurveyRepository { @@ -45,4 +48,10 @@ class SurveyRepositoryImpl implements SurveyRepository { return list.items; } + + @override + Future fetchDetailedSurvey(String id) { + final params = SurveyInfoParams(id: id); + return _surveyApiService.info(params: params); + } } diff --git a/lib/services/api/api_raw_response.dart b/lib/services/api/api_raw_response.dart new file mode 100644 index 00000000..9f19cf18 --- /dev/null +++ b/lib/services/api/api_raw_response.dart @@ -0,0 +1,30 @@ +part of 'api_service.dart'; + +class ApiRawResponse with Mappable { + ApiRawObject? data; + Map? meta; + List? included; + + @override + void mapping(Mapper map) { + map("data", data, (v) => data = v as ApiRawObject?); + map>( + "meta", meta, (v) => meta = v as Map?); + map( + "included", included, (v) => included = v as List?); + } +} + +class ApiRawObject with Mappable { + String? id; + String? type; + Map? attributes; + + @override + void mapping(Mapper map) { + map("id", id, (v) => id = v as String); + map("type", type, (v) => type = v as String); + map>("attributes", attributes, + (v) => attributes = v as Map); + } +} diff --git a/lib/services/api/api_service.dart b/lib/services/api/api_service.dart index ca369a24..004dc3b3 100644 --- a/lib/services/api/api_service.dart +++ b/lib/services/api/api_service.dart @@ -12,6 +12,8 @@ part 'api_service_register.dart'; part 'api_list_object.dart'; +part 'api_raw_response.dart'; + abstract class ApiService { Future call({ required HttpMethod method, @@ -146,17 +148,19 @@ class ApiServiceImpl implements ApiService { } T _convertResponseToObject(Map response) { + if (T == ApiRawResponse) { + return Mapper.fromJson(response).toObject()!; + } + if (response["data"] == null && T.toString() == "void") { return null as T; } - if (response["data"] is! Map || - response["data"]["attributes"] is! Map) { + if (response["data"] is! Map) { throw ApiException.invalidResponseStructure; } - return Mapper.fromJson( - response["data"]["attributes"] as Map) + return Mapper.fromJson(response["data"] as Map) .toObject()!; } @@ -167,9 +171,7 @@ class ApiServiceImpl implements ApiService { } final items = (response["data"] as List) - .where((e) => e["attributes"] is Map) - .map((e) => Mapper.fromJson(e["attributes"] as Map) - .toObject()!) + .map((e) => Mapper.fromJson(e as Map).toObject()!) .toList(); return ApiListObject(items: items); diff --git a/lib/services/api/survey/survey_api_service.dart b/lib/services/api/survey/survey_api_service.dart index 155a7250..e3fbe91f 100644 --- a/lib/services/api/survey/survey_api_service.dart +++ b/lib/services/api/survey/survey_api_service.dart @@ -1,5 +1,7 @@ import 'package:object_mapper/object_mapper.dart'; +import 'package:survey/models/detailed_survey_info.dart'; import 'package:survey/models/survey_info.dart'; +import 'package:survey/models/survey_question_info.dart'; import 'package:survey/services/api/api_service.dart'; import 'package:survey/services/http/http_service.dart'; import 'package:survey/services/locator/locator_service.dart'; @@ -11,7 +13,8 @@ part 'params/survey_info_params.dart'; abstract class SurveyApiService { static const endpoint = "/surveys"; - Future info({required SurveyInfoParams params}); + Future info({required SurveyInfoParams params}); + Future> list({required SurveyListParams params}); } @@ -19,11 +22,26 @@ class SurveyApiServiceImpl implements SurveyApiService { final ApiService _apiService = locator.get(); @override - Future info({required SurveyInfoParams params}) { - return _apiService.call( + Future info({required SurveyInfoParams params}) async { + final rawResponse = await _apiService.call( method: HttpMethod.get, endpoint: "${SurveyApiService.endpoint}/${params.id}", ); + + final info = Mapper.fromJson(rawResponse.data!.toJson()) + .toObject()!; + + final questions = List.empty(growable: true); + final included = (rawResponse.included ?? []) + .where((element) => element.type == "question"); + + for (final ApiRawObject rawObject in included) { + questions.add( + Mapper.fromJson(rawObject.toJson()).toObject()!); + } + info.questions = questions; + + return info; } @override diff --git a/pubspec.lock b/pubspec.lock index 706ceeaf..fac0361e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -475,7 +475,7 @@ packages: name: object_mapper url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0" package_config: dependency: transitive description: @@ -685,7 +685,7 @@ packages: name: skeletor url: "https://pub.dartlang.org" source: hosted - version: "0.0.2" + version: "0.0.3" sky_engine: dependency: transitive description: flutter @@ -732,7 +732,7 @@ packages: name: streams_provider url: "https://pub.dartlang.org" source: hosted - version: "0.1.0" + version: "0.1.1" string_scanner: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 325e9aa0..7c070808 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,14 +35,14 @@ dependencies: get_it: ^7.0.0 json_annotation: ^4.0.1 enumerated_class: ^0.0.1+1 - object_mapper: ^1.1.0 - streams_provider: ^0.1.0 + object_mapper: ^1.2.0 + streams_provider: ^0.1.1 shared_preferences: ^2.0.5 modal_progress_hud_nsn: ^0.1.0-nullsafety-1 adaptive_dialog: ^0.10.0+5 async: ^2.5.0 carousel_slider: ^4.0.0-nullsafety.0 - skeletor: ^0.0.2 + skeletor: ^0.0.3 simple_gesture_detector: ^0.2.0 equatable: ^2.0.2 tuple: ^2.0.0