Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(dart_frog): add tryRead method to RequestContext #1652

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/docs/basics/dependency-injection.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ Response onRequest(RequestContext context) {
}
```

If you want to attempt to `read` a value that has not been provided, it will throw a `StateError`. However, if you want to _attempt_ to `read` a value whether it's provided or not, you can use `context.tryRead<T>()`. If no value matching that time has been provided, it will return `null`.

```dart
import 'package:dart_frog/dart_frog.dart';

Response onRequest(RequestContext context) {
final greeting = context.tryRead<String>();
return Response(body: greeting ?? 'Default Greeting');
}
```

### Extracting Providers

In the above example, we defined the `provider` inline. This is fine for simple cases, but for more complex providers or providers which you want to reuse, it can be helpful to extract the provider to its own file:
Expand Down
15 changes: 15 additions & 0 deletions packages/dart_frog/lib/src/context.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,21 @@ This can happen if $T was not provided to the request context:
return (value as T Function())();
}

/// Attempt to lookup an instance of [T] from the [request] context.
///
/// Returns `null` if [T] is not available within the provided
/// [request] context.
T? tryRead<T>() {
try {
return read<T>();
// Explicitly catching [StateError] as it's what it throw
// when [read] fails
// ignore: avoid_catching_errors
} on StateError catch (_) {
return null;
}
}

/// Get URL parameters captured by the [Router.mount].
/// They can be accessed from inside the mounted routes.
Map<String, String> get mountedParams {
Expand Down
86 changes: 86 additions & 0 deletions packages/dart_frog/test/src/provider_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,90 @@ void main() {

await server.close();
});

group('using tryRead()', () {
test('values can be provided and read via middleware', () async {
const value = '__test_value__';
String? nullableValue;
Handler middleware(Handler handler) {
return handler
.use(provider<String>((_) => value))
.use(provider<String?>((_) => nullableValue));
}

Response onRequest(RequestContext context) {
final value = context.tryRead<String>();
final nullableValue = context.tryRead<String?>();
return Response(body: '$value:$nullableValue');
}

final handler =
const Pipeline().addMiddleware(middleware).addHandler(onRequest);

final server = await serve(handler, 'localhost', 3010);
final client = http.Client();
final response = await client.get(Uri.parse('http://localhost:3010/'));

await expectLater(response.statusCode, equals(HttpStatus.ok));
await expectLater(response.body, equals('$value:$nullableValue'));

await server.close();
});

test('descendant providers can access provided values', () async {
const url = 'http://localhost/';
Handler middleware(Handler handler) {
return handler.use(
provider<Uri?>((context) {
final stringValue = context.tryRead<String>();
return stringValue == null ? null : Uri.parse(stringValue);
}),
).use(provider<String>((context) => url));
}

Response onRequest(RequestContext context) {
final value = context.tryRead<Uri?>();
return Response(body: value.toString());
}

final handler =
const Pipeline().addMiddleware(middleware).addHandler(onRequest);

final server = await serve(handler, 'localhost', 3011);
final client = http.Client();
final response = await client.get(Uri.parse('http://localhost:3011/'));

await expectLater(response.statusCode, equals(HttpStatus.ok));
await expectLater(response.body, equals(url));

await server.close();
});

test('null is returned and no StateError is thrown', () async {
Object? exception;
Uri? value;
Response onRequest(RequestContext context) {
try {
value = context.tryRead<Uri>();
} catch (e) {
exception = e;
}
return Response();
}

final handler = const Pipeline()
.addMiddleware((handler) => handler)
.addHandler(onRequest);

final server = await serve(handler, 'localhost', 3012);
final client = http.Client();
final response = await client.get(Uri.parse('http://localhost:3012/'));

await expectLater(response.statusCode, equals(HttpStatus.ok));
expect(exception, isNull);
expect(value, isNull);

await server.close();
});
});
}
Loading