mirror of https://github.com/flutter/samples.git
Compass App: Add "server" dart shelf-app and "shared" dart package (#2359)
This PR introduces two new subprojects: - `compass_server` under `compass_app/server`. - `compass_model` under `compass_app/model`. **`compass_server`** - Dart server implemented using `shelf`. - Created with the `dart create -t server-shelf` template. - Implements two REST endpoints: - `GET /continent`: Returns the list of `Continent` as JSON. - `GET /destination`: Returns the list of `Destination` as JSON. - Generated Docker files have been removed. - Implemented tests. - TODO: Implement some basic auth. **`compass_model`** - Dart package to share data model classes between the `server` and `app`. - Contains the data model classes (`Continent`, `Destination`). - Generated JSON from/To methods and data classes using `freezed`. - The sole purpose of this package is to host the data model. Other shared code should go in a different package. **Other changes** - Created an API Client to connect to the local dart server. - Created "remote" repositories, which also implement a basic in-memory caching strategy. - Created two dependency configurations, one with local repositories and one with remote repos. - Created two application main targets to select configuration: - `lib/main_development.dart` which starts the app with the "local" data configuration. - `lib/main_staging.dart` which starts the app with the "remove" (local dart server) configuration. - `lib/main.dart` still works as default entry point. - Implemented tests for remote repositories. ## Pre-launch Checklist - [x] I read the [Flutter Style Guide] _recently_, and have followed its advice. - [x] I signed the [CLA]. - [x] I read the [Contributors Guide]. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-devrel channel on [Discord]. <!-- Links --> [Flutter Style Guide]: https://github.com/flutter/flutter/blob/master/docs/contributing/Style-guide-for-Flutter-repo.md [CLA]: https://cla.developers.google.com/ [Discord]: https://github.com/flutter/flutter/blob/master/docs/contributing/Chat.md [Contributors Guide]: https://github.com/flutter/samples/blob/main/CONTRIBUTING.mdpull/2389/head
parent
496b467485
commit
be0b3dc0d1
@ -1,12 +0,0 @@
|
|||||||
class Continent {
|
|
||||||
/// e.g. 'Europe'
|
|
||||||
final String name;
|
|
||||||
|
|
||||||
/// e.g. 'https://rstr.in/google/tripedia/TmR12QdlVTT'
|
|
||||||
final String imageUrl;
|
|
||||||
|
|
||||||
Continent({
|
|
||||||
required this.name,
|
|
||||||
required this.imageUrl,
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
/// Model class for Destination data
|
|
||||||
class Destination {
|
|
||||||
Destination({
|
|
||||||
required this.ref,
|
|
||||||
required this.name,
|
|
||||||
required this.country,
|
|
||||||
required this.continent,
|
|
||||||
required this.knownFor,
|
|
||||||
required this.tags,
|
|
||||||
required this.imageUrl,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// e.g. 'alaska'
|
|
||||||
final String ref;
|
|
||||||
|
|
||||||
/// e.g. 'Alaska'
|
|
||||||
final String name;
|
|
||||||
|
|
||||||
/// e.g. 'United States'
|
|
||||||
final String country;
|
|
||||||
|
|
||||||
/// e.g. 'North America'
|
|
||||||
final String continent;
|
|
||||||
|
|
||||||
/// e.g. 'Alaska is a haven for outdoor enthusiasts ...'
|
|
||||||
final String knownFor;
|
|
||||||
|
|
||||||
/// e.g. ['Mountain', 'Off-the-beaten-path', 'Wildlife watching']
|
|
||||||
final List<String> tags;
|
|
||||||
|
|
||||||
/// e.g. 'https://storage.googleapis.com/tripedia-images/destinations/alaska.jpg'
|
|
||||||
final String imageUrl;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'Destination{ref: $ref, name: $name, country: $country, continent: $continent, knownFor: $knownFor, tags: $tags, imageUrl: $imageUrl}';
|
|
||||||
}
|
|
||||||
|
|
||||||
factory Destination.fromJson(Map<String, dynamic> json) {
|
|
||||||
return Destination(
|
|
||||||
ref: json['ref'] as String,
|
|
||||||
name: json['name'] as String,
|
|
||||||
country: json['country'] as String,
|
|
||||||
continent: json['continent'] as String,
|
|
||||||
knownFor: json['knownFor'] as String,
|
|
||||||
tags: (json['tags'] as List<dynamic>).map((e) => e as String).toList(),
|
|
||||||
imageUrl: json['imageUrl'] as String,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,34 @@
|
|||||||
|
import 'package:compass_model/model.dart';
|
||||||
|
|
||||||
|
import '../../../utils/result.dart';
|
||||||
|
import '../../services/api_client.dart';
|
||||||
|
import 'continent_repository.dart';
|
||||||
|
|
||||||
|
/// Remote data source for [Continent].
|
||||||
|
/// Implements basic local caching.
|
||||||
|
/// See: https://docs.flutter.dev/get-started/fwe/local-caching
|
||||||
|
class ContinentRepositoryRemote implements ContinentRepository {
|
||||||
|
ContinentRepositoryRemote({
|
||||||
|
required ApiClient apiClient,
|
||||||
|
}) : _apiClient = apiClient;
|
||||||
|
|
||||||
|
final ApiClient _apiClient;
|
||||||
|
|
||||||
|
List<Continent>? _cachedData;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Result<List<Continent>>> getContinents() async {
|
||||||
|
if (_cachedData == null) {
|
||||||
|
// No cached data, request continents
|
||||||
|
final result = await _apiClient.getContinents();
|
||||||
|
if (result is Ok) {
|
||||||
|
// Store value if result Ok
|
||||||
|
_cachedData = result.asOk.value;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
// Return cached data if available
|
||||||
|
return Result.ok(_cachedData!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
import 'package:compass_model/model.dart';
|
||||||
|
|
||||||
|
import '../../../utils/result.dart';
|
||||||
|
import '../../services/api_client.dart';
|
||||||
|
import 'destination_repository.dart';
|
||||||
|
|
||||||
|
/// Remote data source for [Destination].
|
||||||
|
/// Implements basic local caching.
|
||||||
|
/// See: https://docs.flutter.dev/get-started/fwe/local-caching
|
||||||
|
class DestinationRepositoryRemote implements DestinationRepository {
|
||||||
|
DestinationRepositoryRemote({
|
||||||
|
required ApiClient apiClient,
|
||||||
|
}) : _apiClient = apiClient;
|
||||||
|
|
||||||
|
final ApiClient _apiClient;
|
||||||
|
|
||||||
|
List<Destination>? _cachedData;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Result<List<Destination>>> getDestinations() async {
|
||||||
|
if (_cachedData == null) {
|
||||||
|
// No cached data, request destinations
|
||||||
|
final result = await _apiClient.getDestinations();
|
||||||
|
if (result is Ok) {
|
||||||
|
// Store value if result Ok
|
||||||
|
_cachedData = result.asOk.value;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
// Return cached data if available
|
||||||
|
return Result.ok(_cachedData!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:compass_model/model.dart';
|
||||||
|
|
||||||
|
import '../../utils/result.dart';
|
||||||
|
|
||||||
|
// TODO: Basic auth request
|
||||||
|
// TODO: Configurable baseurl/host/port
|
||||||
|
class ApiClient {
|
||||||
|
Future<Result<List<Continent>>> getContinents() async {
|
||||||
|
final client = HttpClient();
|
||||||
|
try {
|
||||||
|
final request = await client.get('localhost', 8080, '/continent');
|
||||||
|
final response = await request.close();
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final stringData = await response.transform(utf8.decoder).join();
|
||||||
|
final json = jsonDecode(stringData) as List<dynamic>;
|
||||||
|
return Result.ok(
|
||||||
|
json.map((element) => Continent.fromJson(element)).toList());
|
||||||
|
} else {
|
||||||
|
return Result.error(const HttpException("Invalid response"));
|
||||||
|
}
|
||||||
|
} on Exception catch (error) {
|
||||||
|
return Result.error(error);
|
||||||
|
} finally {
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Result<List<Destination>>> getDestinations() async {
|
||||||
|
final client = HttpClient();
|
||||||
|
try {
|
||||||
|
final request = await client.get('localhost', 8080, '/destination');
|
||||||
|
final response = await request.close();
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final stringData = await response.transform(utf8.decoder).join();
|
||||||
|
final json = jsonDecode(stringData) as List<dynamic>;
|
||||||
|
return Result.ok(
|
||||||
|
json.map((element) => Destination.fromJson(element)).toList());
|
||||||
|
} else {
|
||||||
|
return Result.error(const HttpException("Invalid response"));
|
||||||
|
}
|
||||||
|
} on Exception catch (error) {
|
||||||
|
return Result.error(error);
|
||||||
|
} finally {
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
import 'config/dependencies.dart';
|
||||||
|
import 'main.dart';
|
||||||
|
|
||||||
|
/// Development config entry point.
|
||||||
|
/// Launch with `flutter run --target lib/main_development.dart`.
|
||||||
|
/// Uses local data.
|
||||||
|
void main() {
|
||||||
|
runApp(
|
||||||
|
MultiProvider(
|
||||||
|
providers: providersLocal,
|
||||||
|
child: const MainApp(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
import 'config/dependencies.dart';
|
||||||
|
import 'main.dart';
|
||||||
|
|
||||||
|
/// Staging config entry point.
|
||||||
|
/// Launch with `flutter run --target lib/main_staging.dart`.
|
||||||
|
/// Uses remote data from a server.
|
||||||
|
void main() {
|
||||||
|
runApp(
|
||||||
|
MultiProvider(
|
||||||
|
providers: providersRemote,
|
||||||
|
child: const MainApp(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
import 'package:compass_app/data/repositories/continent/continent_repository.dart';
|
||||||
|
import 'package:compass_app/data/repositories/continent/continent_repository_remote.dart';
|
||||||
|
import 'package:compass_app/utils/result.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
import '../../../util/fakes/services/fake_api_client.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('ContinentRepositoryRemote tests', () {
|
||||||
|
late FakeApiClient apiClient;
|
||||||
|
late ContinentRepository repository;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
apiClient = FakeApiClient();
|
||||||
|
repository = ContinentRepositoryRemote(apiClient: apiClient);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should get continents', () async {
|
||||||
|
final result = await repository.getContinents();
|
||||||
|
expect(result, isA<Ok>());
|
||||||
|
|
||||||
|
final list = result.asOk.value;
|
||||||
|
expect(list.length, 3);
|
||||||
|
|
||||||
|
final destination = list.first;
|
||||||
|
expect(destination.name, 'CONTINENT');
|
||||||
|
|
||||||
|
// Only one request happened
|
||||||
|
expect(apiClient.requestCount, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should get continents from cache', () async {
|
||||||
|
// Request continents once
|
||||||
|
var result = await repository.getContinents();
|
||||||
|
expect(result, isA<Ok>());
|
||||||
|
|
||||||
|
// Request continents another time
|
||||||
|
result = await repository.getContinents();
|
||||||
|
expect(result, isA<Ok>());
|
||||||
|
|
||||||
|
// Only one request happened
|
||||||
|
expect(apiClient.requestCount, 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
import 'package:compass_app/data/repositories/destination/destination_repository.dart';
|
||||||
|
import 'package:compass_app/data/repositories/destination/destination_repository_remote.dart';
|
||||||
|
import 'package:compass_app/utils/result.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
import '../../../util/fakes/services/fake_api_client.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('DestinationRepositoryRemote tests', () {
|
||||||
|
late FakeApiClient apiClient;
|
||||||
|
late DestinationRepository repository;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
apiClient = FakeApiClient();
|
||||||
|
repository = DestinationRepositoryRemote(apiClient: apiClient);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should get destinations', () async {
|
||||||
|
final result = await repository.getDestinations();
|
||||||
|
expect(result, isA<Ok>());
|
||||||
|
|
||||||
|
final list = result.asOk.value;
|
||||||
|
expect(list.length, 2);
|
||||||
|
|
||||||
|
final destination = list.first;
|
||||||
|
expect(destination.name, 'name1');
|
||||||
|
|
||||||
|
// Only one request happened
|
||||||
|
expect(apiClient.requestCount, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should get destinations from cache', () async {
|
||||||
|
// Request destination once
|
||||||
|
var result = await repository.getDestinations();
|
||||||
|
expect(result, isA<Ok>());
|
||||||
|
|
||||||
|
// Request destination another time
|
||||||
|
result = await repository.getDestinations();
|
||||||
|
expect(result, isA<Ok>());
|
||||||
|
|
||||||
|
// Only one request happened
|
||||||
|
expect(apiClient.requestCount, 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
import 'package:compass_app/data/services/api_client.dart';
|
||||||
|
import 'package:compass_app/utils/result.dart';
|
||||||
|
import 'package:compass_model/model.dart';
|
||||||
|
|
||||||
|
class FakeApiClient implements ApiClient {
|
||||||
|
// Should not increase when using cached data
|
||||||
|
int requestCount = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Result<List<Continent>>> getContinents() async {
|
||||||
|
requestCount++;
|
||||||
|
return Result.ok([
|
||||||
|
const Continent(name: 'CONTINENT', imageUrl: 'URL'),
|
||||||
|
const Continent(name: 'CONTINENT2', imageUrl: 'URL'),
|
||||||
|
const Continent(name: 'CONTINENT3', imageUrl: 'URL'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Result<List<Destination>>> getDestinations() async {
|
||||||
|
requestCount++;
|
||||||
|
return Result.ok(
|
||||||
|
[
|
||||||
|
const Destination(
|
||||||
|
ref: 'ref1',
|
||||||
|
name: 'name1',
|
||||||
|
country: 'country1',
|
||||||
|
continent: 'Europe',
|
||||||
|
knownFor: 'knownFor1',
|
||||||
|
tags: ['tags1'],
|
||||||
|
imageUrl: 'imageUrl1',
|
||||||
|
),
|
||||||
|
const Destination(
|
||||||
|
ref: 'ref2',
|
||||||
|
name: 'name2',
|
||||||
|
country: 'country2',
|
||||||
|
continent: 'Europe',
|
||||||
|
knownFor: 'knownFor2',
|
||||||
|
tags: ['tags2'],
|
||||||
|
imageUrl: 'imageUrl2',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
# https://dart.dev/guides/libraries/private-files
|
||||||
|
# Created by `dart pub`
|
||||||
|
.dart_tool/
|
||||||
|
|
||||||
|
# Avoid committing pubspec.lock for library packages; see
|
||||||
|
# https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||||
|
pubspec.lock
|
@ -0,0 +1,3 @@
|
|||||||
|
# compass_model
|
||||||
|
|
||||||
|
Shared Data Model for the `compass_app` example.
|
@ -0,0 +1,30 @@
|
|||||||
|
# This file configures the static analysis results for your project (errors,
|
||||||
|
# warnings, and lints).
|
||||||
|
#
|
||||||
|
# This enables the 'recommended' set of lints from `package:lints`.
|
||||||
|
# This set helps identify many issues that may lead to problems when running
|
||||||
|
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
|
||||||
|
# style and format.
|
||||||
|
#
|
||||||
|
# If you want a smaller set of lints you can change this to specify
|
||||||
|
# 'package:lints/core.yaml'. These are just the most critical lints
|
||||||
|
# (the recommended set includes the core lints).
|
||||||
|
# The core lints are also what is used by pub.dev for scoring packages.
|
||||||
|
|
||||||
|
include: package:lints/recommended.yaml
|
||||||
|
|
||||||
|
# Uncomment the following section to specify additional rules.
|
||||||
|
|
||||||
|
# linter:
|
||||||
|
# rules:
|
||||||
|
# - camel_case_types
|
||||||
|
|
||||||
|
# analyzer:
|
||||||
|
# exclude:
|
||||||
|
# - path/to/excluded/files/**
|
||||||
|
|
||||||
|
# For more information about the core and recommended set of lints, see
|
||||||
|
# https://dart.dev/go/core-lints
|
||||||
|
|
||||||
|
# For additional information about configuring this file, see
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
@ -0,0 +1,4 @@
|
|||||||
|
library;
|
||||||
|
|
||||||
|
export 'src/model/continent/continent.dart';
|
||||||
|
export 'src/model/destination/destination.dart';
|
@ -0,0 +1,19 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
part 'continent.freezed.dart';
|
||||||
|
|
||||||
|
part 'continent.g.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class Continent with _$Continent {
|
||||||
|
const factory Continent({
|
||||||
|
/// e.g. 'Europe'
|
||||||
|
required String name,
|
||||||
|
|
||||||
|
/// e.g. 'https://rstr.in/google/tripedia/TmR12QdlVTT'
|
||||||
|
required String imageUrl,
|
||||||
|
}) = _Continent;
|
||||||
|
|
||||||
|
factory Continent.fromJson(Map<String, Object?> json) =>
|
||||||
|
_$ContinentFromJson(json);
|
||||||
|
}
|
@ -0,0 +1,191 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'continent.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||||
|
|
||||||
|
Continent _$ContinentFromJson(Map<String, dynamic> json) {
|
||||||
|
return _Continent.fromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$Continent {
|
||||||
|
/// e.g. 'Europe'
|
||||||
|
String get name => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// e.g. 'https://rstr.in/google/tripedia/TmR12QdlVTT'
|
||||||
|
String get imageUrl => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Serializes this Continent to a JSON map.
|
||||||
|
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Create a copy of Continent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
$ContinentCopyWith<Continent> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $ContinentCopyWith<$Res> {
|
||||||
|
factory $ContinentCopyWith(Continent value, $Res Function(Continent) then) =
|
||||||
|
_$ContinentCopyWithImpl<$Res, Continent>;
|
||||||
|
@useResult
|
||||||
|
$Res call({String name, String imageUrl});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$ContinentCopyWithImpl<$Res, $Val extends Continent>
|
||||||
|
implements $ContinentCopyWith<$Res> {
|
||||||
|
_$ContinentCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
/// Create a copy of Continent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? name = null,
|
||||||
|
Object? imageUrl = null,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
name: null == name
|
||||||
|
? _value.name
|
||||||
|
: name // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
imageUrl: null == imageUrl
|
||||||
|
? _value.imageUrl
|
||||||
|
: imageUrl // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$ContinentImplCopyWith<$Res>
|
||||||
|
implements $ContinentCopyWith<$Res> {
|
||||||
|
factory _$$ContinentImplCopyWith(
|
||||||
|
_$ContinentImpl value, $Res Function(_$ContinentImpl) then) =
|
||||||
|
__$$ContinentImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call({String name, String imageUrl});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$ContinentImplCopyWithImpl<$Res>
|
||||||
|
extends _$ContinentCopyWithImpl<$Res, _$ContinentImpl>
|
||||||
|
implements _$$ContinentImplCopyWith<$Res> {
|
||||||
|
__$$ContinentImplCopyWithImpl(
|
||||||
|
_$ContinentImpl _value, $Res Function(_$ContinentImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of Continent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? name = null,
|
||||||
|
Object? imageUrl = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$ContinentImpl(
|
||||||
|
name: null == name
|
||||||
|
? _value.name
|
||||||
|
: name // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
imageUrl: null == imageUrl
|
||||||
|
? _value.imageUrl
|
||||||
|
: imageUrl // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
class _$ContinentImpl implements _Continent {
|
||||||
|
const _$ContinentImpl({required this.name, required this.imageUrl});
|
||||||
|
|
||||||
|
factory _$ContinentImpl.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$$ContinentImplFromJson(json);
|
||||||
|
|
||||||
|
/// e.g. 'Europe'
|
||||||
|
@override
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
/// e.g. 'https://rstr.in/google/tripedia/TmR12QdlVTT'
|
||||||
|
@override
|
||||||
|
final String imageUrl;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'Continent(name: $name, imageUrl: $imageUrl)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$ContinentImpl &&
|
||||||
|
(identical(other.name, name) || other.name == name) &&
|
||||||
|
(identical(other.imageUrl, imageUrl) ||
|
||||||
|
other.imageUrl == imageUrl));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType, name, imageUrl);
|
||||||
|
|
||||||
|
/// Create a copy of Continent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$ContinentImplCopyWith<_$ContinentImpl> get copyWith =>
|
||||||
|
__$$ContinentImplCopyWithImpl<_$ContinentImpl>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$$ContinentImplToJson(
|
||||||
|
this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _Continent implements Continent {
|
||||||
|
const factory _Continent(
|
||||||
|
{required final String name,
|
||||||
|
required final String imageUrl}) = _$ContinentImpl;
|
||||||
|
|
||||||
|
factory _Continent.fromJson(Map<String, dynamic> json) =
|
||||||
|
_$ContinentImpl.fromJson;
|
||||||
|
|
||||||
|
/// e.g. 'Europe'
|
||||||
|
@override
|
||||||
|
String get name;
|
||||||
|
|
||||||
|
/// e.g. 'https://rstr.in/google/tripedia/TmR12QdlVTT'
|
||||||
|
@override
|
||||||
|
String get imageUrl;
|
||||||
|
|
||||||
|
/// Create a copy of Continent
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$ContinentImplCopyWith<_$ContinentImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'continent.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
_$ContinentImpl _$$ContinentImplFromJson(Map<String, dynamic> json) =>
|
||||||
|
_$ContinentImpl(
|
||||||
|
name: json['name'] as String,
|
||||||
|
imageUrl: json['imageUrl'] as String,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$ContinentImplToJson(_$ContinentImpl instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'name': instance.name,
|
||||||
|
'imageUrl': instance.imageUrl,
|
||||||
|
};
|
@ -0,0 +1,34 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
part 'destination.freezed.dart';
|
||||||
|
|
||||||
|
part 'destination.g.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class Destination with _$Destination {
|
||||||
|
const factory Destination({
|
||||||
|
/// e.g. 'alaska'
|
||||||
|
required String ref,
|
||||||
|
|
||||||
|
/// e.g. 'Alaska'
|
||||||
|
required String name,
|
||||||
|
|
||||||
|
/// e.g. 'United States'
|
||||||
|
required String country,
|
||||||
|
|
||||||
|
/// e.g. 'North America'
|
||||||
|
required String continent,
|
||||||
|
|
||||||
|
/// e.g. 'Alaska is a haven for outdoor enthusiasts ...'
|
||||||
|
required String knownFor,
|
||||||
|
|
||||||
|
/// e.g. ['Mountain', 'Off-the-beaten-path', 'Wildlife watching']
|
||||||
|
required List<String> tags,
|
||||||
|
|
||||||
|
/// e.g. 'https://storage.googleapis.com/tripedia-images/destinations/alaska.jpg'
|
||||||
|
required String imageUrl,
|
||||||
|
}) = _Destination;
|
||||||
|
|
||||||
|
factory Destination.fromJson(Map<String, Object?> json) =>
|
||||||
|
_$DestinationFromJson(json);
|
||||||
|
}
|
@ -0,0 +1,339 @@
|
|||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'destination.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||||
|
|
||||||
|
Destination _$DestinationFromJson(Map<String, dynamic> json) {
|
||||||
|
return _Destination.fromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$Destination {
|
||||||
|
/// e.g. 'alaska'
|
||||||
|
String get ref => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// e.g. 'Alaska'
|
||||||
|
String get name => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// e.g. 'United States'
|
||||||
|
String get country => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// e.g. 'North America'
|
||||||
|
String get continent => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// e.g. 'Alaska is a haven for outdoor enthusiasts ...'
|
||||||
|
String get knownFor => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// e.g. ['Mountain', 'Off-the-beaten-path', 'Wildlife watching']
|
||||||
|
List<String> get tags => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// e.g. 'https://storage.googleapis.com/tripedia-images/destinations/alaska.jpg'
|
||||||
|
String get imageUrl => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Serializes this Destination to a JSON map.
|
||||||
|
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
|
/// Create a copy of Destination
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
$DestinationCopyWith<Destination> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class $DestinationCopyWith<$Res> {
|
||||||
|
factory $DestinationCopyWith(
|
||||||
|
Destination value, $Res Function(Destination) then) =
|
||||||
|
_$DestinationCopyWithImpl<$Res, Destination>;
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{String ref,
|
||||||
|
String name,
|
||||||
|
String country,
|
||||||
|
String continent,
|
||||||
|
String knownFor,
|
||||||
|
List<String> tags,
|
||||||
|
String imageUrl});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class _$DestinationCopyWithImpl<$Res, $Val extends Destination>
|
||||||
|
implements $DestinationCopyWith<$Res> {
|
||||||
|
_$DestinationCopyWithImpl(this._value, this._then);
|
||||||
|
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Val _value;
|
||||||
|
// ignore: unused_field
|
||||||
|
final $Res Function($Val) _then;
|
||||||
|
|
||||||
|
/// Create a copy of Destination
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? ref = null,
|
||||||
|
Object? name = null,
|
||||||
|
Object? country = null,
|
||||||
|
Object? continent = null,
|
||||||
|
Object? knownFor = null,
|
||||||
|
Object? tags = null,
|
||||||
|
Object? imageUrl = null,
|
||||||
|
}) {
|
||||||
|
return _then(_value.copyWith(
|
||||||
|
ref: null == ref
|
||||||
|
? _value.ref
|
||||||
|
: ref // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
name: null == name
|
||||||
|
? _value.name
|
||||||
|
: name // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
country: null == country
|
||||||
|
? _value.country
|
||||||
|
: country // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
continent: null == continent
|
||||||
|
? _value.continent
|
||||||
|
: continent // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
knownFor: null == knownFor
|
||||||
|
? _value.knownFor
|
||||||
|
: knownFor // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
tags: null == tags
|
||||||
|
? _value.tags
|
||||||
|
: tags // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<String>,
|
||||||
|
imageUrl: null == imageUrl
|
||||||
|
? _value.imageUrl
|
||||||
|
: imageUrl // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
) as $Val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract class _$$DestinationImplCopyWith<$Res>
|
||||||
|
implements $DestinationCopyWith<$Res> {
|
||||||
|
factory _$$DestinationImplCopyWith(
|
||||||
|
_$DestinationImpl value, $Res Function(_$DestinationImpl) then) =
|
||||||
|
__$$DestinationImplCopyWithImpl<$Res>;
|
||||||
|
@override
|
||||||
|
@useResult
|
||||||
|
$Res call(
|
||||||
|
{String ref,
|
||||||
|
String name,
|
||||||
|
String country,
|
||||||
|
String continent,
|
||||||
|
String knownFor,
|
||||||
|
List<String> tags,
|
||||||
|
String imageUrl});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
class __$$DestinationImplCopyWithImpl<$Res>
|
||||||
|
extends _$DestinationCopyWithImpl<$Res, _$DestinationImpl>
|
||||||
|
implements _$$DestinationImplCopyWith<$Res> {
|
||||||
|
__$$DestinationImplCopyWithImpl(
|
||||||
|
_$DestinationImpl _value, $Res Function(_$DestinationImpl) _then)
|
||||||
|
: super(_value, _then);
|
||||||
|
|
||||||
|
/// Create a copy of Destination
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
@override
|
||||||
|
$Res call({
|
||||||
|
Object? ref = null,
|
||||||
|
Object? name = null,
|
||||||
|
Object? country = null,
|
||||||
|
Object? continent = null,
|
||||||
|
Object? knownFor = null,
|
||||||
|
Object? tags = null,
|
||||||
|
Object? imageUrl = null,
|
||||||
|
}) {
|
||||||
|
return _then(_$DestinationImpl(
|
||||||
|
ref: null == ref
|
||||||
|
? _value.ref
|
||||||
|
: ref // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
name: null == name
|
||||||
|
? _value.name
|
||||||
|
: name // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
country: null == country
|
||||||
|
? _value.country
|
||||||
|
: country // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
continent: null == continent
|
||||||
|
? _value.continent
|
||||||
|
: continent // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
knownFor: null == knownFor
|
||||||
|
? _value.knownFor
|
||||||
|
: knownFor // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
tags: null == tags
|
||||||
|
? _value._tags
|
||||||
|
: tags // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<String>,
|
||||||
|
imageUrl: null == imageUrl
|
||||||
|
? _value.imageUrl
|
||||||
|
: imageUrl // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
class _$DestinationImpl implements _Destination {
|
||||||
|
const _$DestinationImpl(
|
||||||
|
{required this.ref,
|
||||||
|
required this.name,
|
||||||
|
required this.country,
|
||||||
|
required this.continent,
|
||||||
|
required this.knownFor,
|
||||||
|
required final List<String> tags,
|
||||||
|
required this.imageUrl})
|
||||||
|
: _tags = tags;
|
||||||
|
|
||||||
|
factory _$DestinationImpl.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$$DestinationImplFromJson(json);
|
||||||
|
|
||||||
|
/// e.g. 'alaska'
|
||||||
|
@override
|
||||||
|
final String ref;
|
||||||
|
|
||||||
|
/// e.g. 'Alaska'
|
||||||
|
@override
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
/// e.g. 'United States'
|
||||||
|
@override
|
||||||
|
final String country;
|
||||||
|
|
||||||
|
/// e.g. 'North America'
|
||||||
|
@override
|
||||||
|
final String continent;
|
||||||
|
|
||||||
|
/// e.g. 'Alaska is a haven for outdoor enthusiasts ...'
|
||||||
|
@override
|
||||||
|
final String knownFor;
|
||||||
|
|
||||||
|
/// e.g. ['Mountain', 'Off-the-beaten-path', 'Wildlife watching']
|
||||||
|
final List<String> _tags;
|
||||||
|
|
||||||
|
/// e.g. ['Mountain', 'Off-the-beaten-path', 'Wildlife watching']
|
||||||
|
@override
|
||||||
|
List<String> get tags {
|
||||||
|
if (_tags is EqualUnmodifiableListView) return _tags;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableListView(_tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// e.g. 'https://storage.googleapis.com/tripedia-images/destinations/alaska.jpg'
|
||||||
|
@override
|
||||||
|
final String imageUrl;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'Destination(ref: $ref, name: $name, country: $country, continent: $continent, knownFor: $knownFor, tags: $tags, imageUrl: $imageUrl)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) ||
|
||||||
|
(other.runtimeType == runtimeType &&
|
||||||
|
other is _$DestinationImpl &&
|
||||||
|
(identical(other.ref, ref) || other.ref == ref) &&
|
||||||
|
(identical(other.name, name) || other.name == name) &&
|
||||||
|
(identical(other.country, country) || other.country == country) &&
|
||||||
|
(identical(other.continent, continent) ||
|
||||||
|
other.continent == continent) &&
|
||||||
|
(identical(other.knownFor, knownFor) ||
|
||||||
|
other.knownFor == knownFor) &&
|
||||||
|
const DeepCollectionEquality().equals(other._tags, _tags) &&
|
||||||
|
(identical(other.imageUrl, imageUrl) ||
|
||||||
|
other.imageUrl == imageUrl));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType, ref, name, country, continent,
|
||||||
|
knownFor, const DeepCollectionEquality().hash(_tags), imageUrl);
|
||||||
|
|
||||||
|
/// Create a copy of Destination
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$$DestinationImplCopyWith<_$DestinationImpl> get copyWith =>
|
||||||
|
__$$DestinationImplCopyWithImpl<_$DestinationImpl>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$$DestinationImplToJson(
|
||||||
|
this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _Destination implements Destination {
|
||||||
|
const factory _Destination(
|
||||||
|
{required final String ref,
|
||||||
|
required final String name,
|
||||||
|
required final String country,
|
||||||
|
required final String continent,
|
||||||
|
required final String knownFor,
|
||||||
|
required final List<String> tags,
|
||||||
|
required final String imageUrl}) = _$DestinationImpl;
|
||||||
|
|
||||||
|
factory _Destination.fromJson(Map<String, dynamic> json) =
|
||||||
|
_$DestinationImpl.fromJson;
|
||||||
|
|
||||||
|
/// e.g. 'alaska'
|
||||||
|
@override
|
||||||
|
String get ref;
|
||||||
|
|
||||||
|
/// e.g. 'Alaska'
|
||||||
|
@override
|
||||||
|
String get name;
|
||||||
|
|
||||||
|
/// e.g. 'United States'
|
||||||
|
@override
|
||||||
|
String get country;
|
||||||
|
|
||||||
|
/// e.g. 'North America'
|
||||||
|
@override
|
||||||
|
String get continent;
|
||||||
|
|
||||||
|
/// e.g. 'Alaska is a haven for outdoor enthusiasts ...'
|
||||||
|
@override
|
||||||
|
String get knownFor;
|
||||||
|
|
||||||
|
/// e.g. ['Mountain', 'Off-the-beaten-path', 'Wildlife watching']
|
||||||
|
@override
|
||||||
|
List<String> get tags;
|
||||||
|
|
||||||
|
/// e.g. 'https://storage.googleapis.com/tripedia-images/destinations/alaska.jpg'
|
||||||
|
@override
|
||||||
|
String get imageUrl;
|
||||||
|
|
||||||
|
/// Create a copy of Destination
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
_$$DestinationImplCopyWith<_$DestinationImpl> get copyWith =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'destination.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// JsonSerializableGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
_$DestinationImpl _$$DestinationImplFromJson(Map<String, dynamic> json) =>
|
||||||
|
_$DestinationImpl(
|
||||||
|
ref: json['ref'] as String,
|
||||||
|
name: json['name'] as String,
|
||||||
|
country: json['country'] as String,
|
||||||
|
continent: json['continent'] as String,
|
||||||
|
knownFor: json['knownFor'] as String,
|
||||||
|
tags: (json['tags'] as List<dynamic>).map((e) => e as String).toList(),
|
||||||
|
imageUrl: json['imageUrl'] as String,
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$$DestinationImplToJson(_$DestinationImpl instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'ref': instance.ref,
|
||||||
|
'name': instance.name,
|
||||||
|
'country': instance.country,
|
||||||
|
'continent': instance.continent,
|
||||||
|
'knownFor': instance.knownFor,
|
||||||
|
'tags': instance.tags,
|
||||||
|
'imageUrl': instance.imageUrl,
|
||||||
|
};
|
@ -0,0 +1,17 @@
|
|||||||
|
name: compass_model
|
||||||
|
description: Compass App Data Model
|
||||||
|
publish_to: 'none'
|
||||||
|
version: 1.0.0
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ^3.4.3
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
build_runner: ^2.4.11
|
||||||
|
freezed: ^2.5.7
|
||||||
|
json_serializable: ^6.8.0
|
||||||
|
lints: ^3.0.0
|
||||||
|
test: ^1.24.0
|
||||||
|
dependencies:
|
||||||
|
freezed_annotation: ^2.4.4
|
||||||
|
json_annotation: ^4.9.0
|
@ -0,0 +1,3 @@
|
|||||||
|
# https://dart.dev/guides/libraries/private-files
|
||||||
|
# Created by `dart pub`
|
||||||
|
.dart_tool/
|
@ -0,0 +1,13 @@
|
|||||||
|
A server app built using [Shelf](https://pub.dev/packages/shelf).
|
||||||
|
|
||||||
|
# Running the server
|
||||||
|
|
||||||
|
## Running with the Dart SDK
|
||||||
|
|
||||||
|
You can run the example with the [Dart SDK](https://dart.dev/get-dart)
|
||||||
|
like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ dart run
|
||||||
|
Server listening on port 8080
|
||||||
|
```
|
@ -0,0 +1,30 @@
|
|||||||
|
# This file configures the static analysis results for your project (errors,
|
||||||
|
# warnings, and lints).
|
||||||
|
#
|
||||||
|
# This enables the 'recommended' set of lints from `package:lints`.
|
||||||
|
# This set helps identify many issues that may lead to problems when running
|
||||||
|
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
|
||||||
|
# style and format.
|
||||||
|
#
|
||||||
|
# If you want a smaller set of lints you can change this to specify
|
||||||
|
# 'package:lints/core.yaml'. These are just the most critical lints
|
||||||
|
# (the recommended set includes the core lints).
|
||||||
|
# The core lints are also what is used by pub.dev for scoring packages.
|
||||||
|
|
||||||
|
include: package:lints/recommended.yaml
|
||||||
|
|
||||||
|
# Uncomment the following section to specify additional rules.
|
||||||
|
|
||||||
|
# linter:
|
||||||
|
# rules:
|
||||||
|
# - camel_case_types
|
||||||
|
|
||||||
|
# analyzer:
|
||||||
|
# exclude:
|
||||||
|
# - path/to/excluded/files/**
|
||||||
|
|
||||||
|
# For more information about the core and recommended set of lints, see
|
||||||
|
# https://dart.dev/go/core-lints
|
||||||
|
|
||||||
|
# For additional information about configuring this file, see
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,26 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:compass_server/routes/continent.dart';
|
||||||
|
import 'package:compass_server/routes/destination.dart';
|
||||||
|
import 'package:shelf/shelf.dart';
|
||||||
|
import 'package:shelf/shelf_io.dart';
|
||||||
|
import 'package:shelf_router/shelf_router.dart';
|
||||||
|
|
||||||
|
// Configure routes.
|
||||||
|
final _router = Router()
|
||||||
|
..get('/continent', continentHandler)
|
||||||
|
..get('/destination', destinationHandler);
|
||||||
|
|
||||||
|
void main(List<String> args) async {
|
||||||
|
// Use any available host or container IP (usually `0.0.0.0`).
|
||||||
|
final ip = InternetAddress.anyIPv4;
|
||||||
|
|
||||||
|
// Configure a pipeline that logs requests.
|
||||||
|
final handler =
|
||||||
|
Pipeline().addMiddleware(logRequests()).addHandler(_router.call);
|
||||||
|
|
||||||
|
// For running in containers, we respect the PORT environment variable.
|
||||||
|
final port = int.parse(Platform.environment['PORT'] ?? '8080');
|
||||||
|
final server = await serve(handler, ip, port);
|
||||||
|
print('Server listening on port ${server.port}');
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:shelf/shelf.dart';
|
||||||
|
import 'package:compass_model/model.dart';
|
||||||
|
|
||||||
|
final _continents = [
|
||||||
|
Continent(
|
||||||
|
name: 'Europe',
|
||||||
|
imageUrl: 'https://rstr.in/google/tripedia/TmR12QdlVTT',
|
||||||
|
),
|
||||||
|
Continent(
|
||||||
|
name: 'Asia',
|
||||||
|
imageUrl: 'https://rstr.in/google/tripedia/VJ8BXlQg8O1',
|
||||||
|
),
|
||||||
|
Continent(
|
||||||
|
name: 'South America',
|
||||||
|
imageUrl: 'https://rstr.in/google/tripedia/flm_-o1aI8e',
|
||||||
|
),
|
||||||
|
Continent(
|
||||||
|
name: 'Africa',
|
||||||
|
imageUrl: 'https://rstr.in/google/tripedia/-nzi8yFOBpF',
|
||||||
|
),
|
||||||
|
Continent(
|
||||||
|
name: 'North America',
|
||||||
|
imageUrl: 'https://rstr.in/google/tripedia/jlbgFDrSUVE',
|
||||||
|
),
|
||||||
|
Continent(
|
||||||
|
name: 'Oceania',
|
||||||
|
imageUrl: 'https://rstr.in/google/tripedia/vxyrDE-fZVL',
|
||||||
|
),
|
||||||
|
Continent(
|
||||||
|
name: 'Australia',
|
||||||
|
imageUrl: 'https://rstr.in/google/tripedia/z6vy6HeRyvZ',
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
Response continentHandler(Request req) {
|
||||||
|
return Response.ok(jsonEncode(_continents));
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:shelf/shelf.dart';
|
||||||
|
|
||||||
|
Future<Response> destinationHandler(Request req) async {
|
||||||
|
final file = File('assets/destinations.json');
|
||||||
|
final jsonString = await file.readAsString();
|
||||||
|
return Response.ok(jsonString);
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
name: compass_server
|
||||||
|
description: A server app using the shelf package and Docker.
|
||||||
|
publish_to: 'none'
|
||||||
|
version: 1.0.0
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ^3.4.3
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
args: ^2.4.0
|
||||||
|
shelf: ^1.4.0
|
||||||
|
shelf_router: ^1.1.0
|
||||||
|
compass_model:
|
||||||
|
path: ../model
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
http: ^1.1.0
|
||||||
|
lints: ^3.0.0
|
||||||
|
test: ^1.24.0
|
@ -0,0 +1,53 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:compass_model/model.dart';
|
||||||
|
import 'package:http/http.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
final port = '8080';
|
||||||
|
final host = 'http://0.0.0.0:$port';
|
||||||
|
late Process p;
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
p = await Process.start(
|
||||||
|
'dart',
|
||||||
|
['run', 'bin/compass_server.dart'],
|
||||||
|
environment: {'PORT': port},
|
||||||
|
);
|
||||||
|
// Wait for server to start and print to stdout.
|
||||||
|
await p.stdout.first;
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() => p.kill());
|
||||||
|
|
||||||
|
test('Get Continent end-point', () async {
|
||||||
|
// Query /continent end-point
|
||||||
|
final response = await get(Uri.parse('$host/continent'));
|
||||||
|
expect(response.statusCode, 200);
|
||||||
|
// Parse json response list
|
||||||
|
final list = jsonDecode(response.body) as List<dynamic>;
|
||||||
|
// Parse items
|
||||||
|
final continents = list.map((element) => Continent.fromJson(element));
|
||||||
|
expect(continents.length, 7);
|
||||||
|
expect(continents.first.name, 'Europe');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Get Destination end-point', () async {
|
||||||
|
// Query /destination end-point
|
||||||
|
final response = await get(Uri.parse('$host/destination'));
|
||||||
|
expect(response.statusCode, 200);
|
||||||
|
// Parse json response list
|
||||||
|
final list = jsonDecode(response.body) as List<dynamic>;
|
||||||
|
// Parse items
|
||||||
|
final destination = list.map((element) => Destination.fromJson(element));
|
||||||
|
expect(destination.length, 137);
|
||||||
|
expect(destination.first.name, 'Alaska');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('404', () async {
|
||||||
|
final response = await get(Uri.parse('$host/foobar'));
|
||||||
|
expect(response.statusCode, 404);
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in new issue