Skip to content
This repository has been archived by the owner on Oct 22, 2024. It is now read-only.

Commit

Permalink
feat: speakers view (#26)
Browse files Browse the repository at this point in the history
  • Loading branch information
verygoodstefan authored Sep 3, 2024
1 parent c1efb3e commit 1c92440
Show file tree
Hide file tree
Showing 32 changed files with 855 additions and 39 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/api_verify_and_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ on:
- "api/**"
- "api/packages/fluttercon_data_source/**"
- "api/packages/fluttercon_shared_models/**"
- "api/packages/speakers_repository/**"
- "api/packages/talks_repository/**"
- "api/packages/user_repository/**"
push:
Expand All @@ -25,6 +26,7 @@ on:
- "api/**"
- "api/packages/fluttercon_data_source/**"
- "api/packages/fluttercon_shared_models/**"
- "api/packages/speakers_repository/**"
- "api/packages/talks_repository/**"
- "api/packages/user_repository/**"

Expand Down
34 changes: 34 additions & 0 deletions .github/workflows/speakers_repository_verify_and_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Speakers Repository

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

on:
pull_request:
branches:
- master
- main
paths:
- ".github/workflows/speakers_repository_verify_and_test.yaml"
- "api/packages/speakers_repository/**"
- "api/packages/fluttercon_data_source/**"
- "api/packages/fluttercon_shared_models/**"
push:
branches:
- master
- main
paths:
- ".github/workflows/**"
- "api/packages/speakers_repository/**"
- "api/packages/fluttercon_data_source/**"
- "api/packages/fluttercon_shared_models/**"

jobs:
verify_and_test:
name: Verify and Test
uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/dart_package.yml@main
with:
working_directory: api/packages/speakers_repository
dart_sdk: stable
min_coverage: 100
62 changes: 62 additions & 0 deletions api/packages/speakers_repository/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Speakers Repository

[![style: very good analysis][very_good_analysis_badge]][very_good_analysis_link]
[![Powered by Mason](https://img.shields.io/endpoint?url=https%3A%2F%2Ftinyurl.com%2Fmason-badge)](https://github.com/felangel/mason)
[![License: MIT][license_badge]][license_link]

A package encapsulating model manipulation for speaker entities for a demo app for [FlutterCon USA 2024][https://flutterconusa.dev/].

## Installation 💻

**❗ In order to start using Speakers Repository you must have the [Dart SDK][dart_install_link] installed on your machine.**

Install via `dart pub add`:

```sh
dart pub add speakers_repository
```

---

## Continuous Integration 🤖

Speakers Repository comes with a built-in [GitHub Actions workflow][github_actions_link] powered by [Very Good Workflows][very_good_workflows_link] but you can also add your preferred CI/CD solution.

Out of the box, on each pull request and push, the CI `formats`, `lints`, and `tests` the code. This ensures the code remains consistent and behaves correctly as you add functionality or make changes. The project uses [Very Good Analysis][very_good_analysis_link] for a strict set of analysis options used by our team. Code coverage is enforced using the [Very Good Workflows][very_good_coverage_link].

---

## Running Tests 🧪

To run all unit tests:

```sh
dart pub global activate coverage 1.2.0
dart test --coverage=coverage
dart pub global run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info
```

To view the generated coverage report you can use [lcov](https://github.com/linux-test-project/lcov).

```sh
# Generate Coverage Report
genhtml coverage/lcov.info -o coverage/

# Open Coverage Report
open coverage/index.html
```

[dart_install_link]: https://dart.dev/get-dart
[github_actions_link]: https://docs.github.com/en/actions/learn-github-actions
[license_badge]: https://img.shields.io/badge/license-MIT-blue.svg
[license_link]: https://opensource.org/licenses/MIT
[logo_black]: https://raw.githubusercontent.com/VGVentures/very_good_brand/main/styles/README/vgv_logo_black.png#gh-light-mode-only
[logo_white]: https://raw.githubusercontent.com/VGVentures/very_good_brand/main/styles/README/vgv_logo_white.png#gh-dark-mode-only
[mason_link]: https://github.com/felangel/mason
[very_good_analysis_badge]: https://img.shields.io/badge/style-very_good_analysis-B22C89.svg
[very_good_analysis_link]: https://pub.dev/packages/very_good_analysis
[very_good_coverage_link]: https://github.com/marketplace/actions/very-good-coverage
[very_good_ventures_link]: https://verygood.ventures
[very_good_ventures_link_light]: https://verygood.ventures#gh-light-mode-only
[very_good_ventures_link_dark]: https://verygood.ventures#gh-dark-mode-only
[very_good_workflows_link]: https://github.com/VeryGoodOpenSource/very_good_workflows
1 change: 1 addition & 0 deletions api/packages/speakers_repository/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include: package:very_good_analysis/analysis_options.6.0.0.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export 'src/speakers_repository.dart';
39 changes: 39 additions & 0 deletions api/packages/speakers_repository/lib/src/speakers_repository.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import 'package:fluttercon_data_source/fluttercon_data_source.dart';
import 'package:fluttercon_shared_models/fluttercon_shared_models.dart';

/// {@template speakers_repository}
/// A repository to cache and prepare speaker data retrieved from the api.
/// {@endtemplate}
class SpeakersRepository {
/// {@macro speakers_repository}
const SpeakersRepository({
required FlutterconDataSource dataSource,
}) : _dataSource = dataSource;

final FlutterconDataSource _dataSource;

/// Fetches a paginated list of speakers from the data source.
Future<PaginatedData<SpeakerPreview>> getSpeakers() async {
final speakersResponse = await _dataSource.getSpeakers();

final speakerPreviews = speakersResponse.items
.map(
(speaker) => SpeakerPreview(
id: speaker?.id ?? '',
name: speaker?.name ?? '',
title: speaker?.title ?? '',
imageUrl: speaker?.imageUrl ?? '',
),
)
.toList();

final sortedSpeakerPreviews = [...speakerPreviews]
..sort((a, b) => a.name.compareTo(b.name));

return PaginatedData(
items: sortedSpeakerPreviews,
limit: speakersResponse.limit,
nextToken: speakersResponse.nextToken,
);
}
}
18 changes: 18 additions & 0 deletions api/packages/speakers_repository/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: speakers_repository
description: Model manipulation for talk entities for FlutterCon demo app.
version: 0.1.0+1
publish_to: none

environment:
sdk: ^3.4.0

dev_dependencies:
mocktail: ^1.0.4
test: ^1.25.7
very_good_analysis: ^6.0.0

dependencies:
fluttercon_data_source:
path: ../fluttercon_data_source
fluttercon_shared_models:
path: ../fluttercon_shared_models
55 changes: 55 additions & 0 deletions api/packages/speakers_repository/test/helpers/test_helpers.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import 'package:fluttercon_data_source/fluttercon_data_source.dart';
import 'package:fluttercon_shared_models/fluttercon_shared_models.dart';

class TestHelpers {
static final speakers = PaginatedResult(
[
Speaker(
id: '1',
name: 'John Doe',
title: 'Test Title 1',
imageUrl: 'Test Image Url 1',
),
Speaker(
id: '2',
name: 'Jane Doe',
title: 'Test Title 2',
imageUrl: 'Test Image Url 2',
),
Speaker(
id: '3',
name: 'John Smith',
title: 'Test Title 3',
imageUrl: 'Test Image Url 3',
),
],
null,
null,
null,
Speaker.classType,
null,
);

static const speakerPreviews = PaginatedData(
items: [
SpeakerPreview(
id: '2',
name: 'Jane Doe',
title: 'Test Title 2',
imageUrl: 'Test Image Url 2',
),
SpeakerPreview(
id: '1',
name: 'John Doe',
title: 'Test Title 1',
imageUrl: 'Test Image Url 1',
),
SpeakerPreview(
id: '3',
name: 'John Smith',
title: 'Test Title 3',
imageUrl: 'Test Image Url 3',
),
],
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// ignore_for_file: prefer_const_constructors
import 'package:fluttercon_data_source/fluttercon_data_source.dart';
import 'package:fluttercon_shared_models/fluttercon_shared_models.dart';
import 'package:mocktail/mocktail.dart';
import 'package:speakers_repository/speakers_repository.dart';
import 'package:test/test.dart';

import '../helpers/test_helpers.dart';

class _MockFlutterconDataSource extends Mock implements FlutterconDataSource {}

void main() {
group('SpeakersRepository', () {
late FlutterconDataSource dataSource;
late SpeakersRepository speakersRepository;

setUp(() {
dataSource = _MockFlutterconDataSource();
speakersRepository = SpeakersRepository(dataSource: dataSource);
});

test('can be instantiated', () {
expect(speakersRepository, isNotNull);
});

group('getTalks', () {
test('returns sorted ${PaginatedData<SpeakerPreview>} when successful',
() async {
when(() => dataSource.getSpeakers())
.thenAnswer((_) async => TestHelpers.speakers);

final result = await speakersRepository.getSpeakers();
expect(result, equals(TestHelpers.speakerPreviews));
});
});
});
}
2 changes: 0 additions & 2 deletions api/packages/talks_repository/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ dev_dependencies:
very_good_analysis: ^6.0.0

dependencies:
equatable: ^2.0.5
fluttercon_data_source:
path: ../fluttercon_data_source
fluttercon_shared_models:
path: ../fluttercon_shared_models
json_annotation: ^4.8.1
2 changes: 2 additions & 0 deletions api/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ dependencies:
fluttercon_shared_models:
path: packages/fluttercon_shared_models
shelf_cors_headers: ^0.1.5
speakers_repository:
path: packages/speakers_repository
talks_repository:
path: packages/talks_repository
user_repository:
Expand Down
8 changes: 8 additions & 0 deletions api/routes/_middleware.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,20 @@ import 'package:dart_frog/dart_frog.dart';
import 'package:dart_frog_auth/dart_frog_auth.dart';
import 'package:fluttercon_data_source/fluttercon_data_source.dart';
import 'package:shelf_cors_headers/shelf_cors_headers.dart';
import 'package:speakers_repository/speakers_repository.dart';
import 'package:talks_repository/talks_repository.dart';
import 'package:user_repository/user_repository.dart';

Handler middleware(Handler handler) {
return handler
.use(requestLogger())
.use(
provider<SpeakersRepository>(
(context) => SpeakersRepository(
dataSource: context.read<FlutterconDataSource>(),
),
),
)
.use(
provider<TalksRepository>(
(context) => TalksRepository(
Expand Down
12 changes: 8 additions & 4 deletions api/routes/speakers/index.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:io';

import 'package:dart_frog/dart_frog.dart';
import 'package:fluttercon_data_source/fluttercon_data_source.dart';
import 'package:speakers_repository/speakers_repository.dart';

Future<Response> onRequest(RequestContext context) async {
return switch (context.request.method) {
Expand All @@ -12,14 +13,17 @@ Future<Response> onRequest(RequestContext context) async {
}

Future<Response> _get(RequestContext context) async {
final dataSource = context.read<FlutterconDataSource>();
final speakersRepo = context.read<SpeakersRepository>();
try {
final data = await dataSource.getSpeakers();
return Response(body: jsonEncode(data.toJson()));
final data = await speakersRepo.getSpeakers();
final json = data.toJson(
(value) => value.toJson(),
);
return Response(body: jsonEncode(json));
} on AmplifyApiException catch (e) {
return Response(
statusCode: HttpStatus.internalServerError,
body: jsonEncode(e.exception),
body: jsonEncode(e.exception.toString()),
);
}
}
Loading

0 comments on commit 1c92440

Please sign in to comment.