Skip to content

Commit

Permalink
fetch pokemon with validation example
Browse files Browse the repository at this point in the history
  • Loading branch information
SandroMaglione committed Mar 19, 2024
1 parent 1565436 commit f39ee8b
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 5 deletions.
7 changes: 7 additions & 0 deletions examples/poke_api/lib/constants.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
abstract interface class Constants {
static const int minimumPokemonId = 1;
static const int maximumPokemonId = 898;

static String requestAPIUrl(int pokemonId) =>
'https://pokeapi.co/api/v2/pokemon/$pokemonId';
}
51 changes: 51 additions & 0 deletions examples/poke_api/lib/main.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import 'dart:convert';

import 'package:fpdart/fpdart.dart';
import 'package:poke_api/constants.dart';
import 'package:poke_api/pokemon.dart';
import 'package:poke_api/pokemon_error.dart';

abstract interface class HttpClient {
String get(Uri uri);
}

Effect<(HttpClient, JsonCodec), PokemonError, Pokemon> program(
String pokemonId,
) =>
Effect.gen(($) async {
final (client, json) = await $(Effect.env());

final id = await $(
Either.fromNullable(
int.tryParse(pokemonId),
PokemonIdNotInt.new,
),
);

if (id < Constants.minimumPokemonId && id > Constants.maximumPokemonId) {
return await $(Effect.fail(const InvalidPokemonIdRange()));
}

final uri = Uri.parse(Constants.requestAPIUrl(id));
final body = await $(Effect.tryCatch(
execute: () => client.get(uri),
onError: (_, __) => const GetPokemonRequestError(),
));

final bodyJson = await $(Either.tryCatch(
execute: () => json.decode(body),
onError: (_, __) => const PokemonJsonDecodeError(),
));

final bodyJsonMap = await $<Map<String, dynamic>>(
Either.safeCastStrict(
bodyJson,
(value) => const PokemonJsonInvalidMap(),
),
);

return $(Effect.tryCatch(
execute: () => Pokemon.fromJson(bodyJsonMap),
onError: (_, __) => const PokemonInvalidJsonModel(),
));
});
20 changes: 20 additions & 0 deletions examples/poke_api/lib/pokemon.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
class Pokemon {
final int id;
final String name;
final int height;
final int weight;

const Pokemon({
required this.id,
required this.name,
required this.height,
required this.weight,
});

factory Pokemon.fromJson(Map<String, Object?> json) => Pokemon(
id: json['id'] as int,
name: json['name'] as String,
height: json['height'] as int,
weight: json['weight'] as int,
);
}
27 changes: 27 additions & 0 deletions examples/poke_api/lib/pokemon_error.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
sealed class PokemonError {
const PokemonError();
}

class PokemonIdNotInt extends PokemonError {
const PokemonIdNotInt();
}

class InvalidPokemonIdRange extends PokemonError {
const InvalidPokemonIdRange();
}

class GetPokemonRequestError extends PokemonError {
const GetPokemonRequestError();
}

class PokemonJsonDecodeError extends PokemonError {
const PokemonJsonDecodeError();
}

class PokemonJsonInvalidMap extends PokemonError {
const PokemonJsonInvalidMap();
}

class PokemonInvalidJsonModel extends PokemonError {
const PokemonInvalidJsonModel();
}
18 changes: 18 additions & 0 deletions examples/poke_api/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: poke_api
description: >
Example of using fpdart to fetch pokemon from pokeapi with validation.
version: 2.0.0
homepage: https://www.sandromaglione.com/
repository: https://github.com/SandroMaglione/fpdart
publish_to: "none"

environment:
sdk: ">=3.3.0 <4.0.0"

dependencies:
fpdart:
path: ../../packages/fpdart

dev_dependencies:
lints: ^2.0.1
test: ^1.23.1
10 changes: 5 additions & 5 deletions packages/fpdart/lib/src/either.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ sealed class Either<L, R> extends IEffect<Never, L, R> {
factory Either.fromNullable(R? r, L Function() onNull) =>
r != null ? Right(r) : Left(onNull());

factory Either.tryCatch(
R Function() run,
L Function(Object o, StackTrace s) onError,
) {
factory Either.tryCatch({
required R Function() execute,
required L Function(Object o, StackTrace s) onError,
}) {
try {
return Right(run());
return Right(execute());
} catch (e, s) {
return Left(onError(e, s));
}
Expand Down

0 comments on commit f39ee8b

Please sign in to comment.