Skip to content

Commit 0d3ed71

Browse files
committed
feat: Added shelf_routing and shelf_routing_generator packages
1 parent cc2df4b commit 0d3ed71

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+2011
-0
lines changed
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# IntelliJ related
2+
.idea/
3+
*.iml
4+
*.ipr
5+
*.iws
6+
7+
# The .vscode folder contains launch configuration and tasks you configure in
8+
# VS Code which you may wish to be included in version control, so this line
9+
# is commented out by default.
10+
#.vscode/
11+
12+
13+
# Files and directories created by pub.
14+
.dart_tool/
15+
.packages
16+
17+
# Conventional directory for build outputs.
18+
build/
19+
20+
# Avoid committing pubspec.lock for library packages; see
21+
# https://dart.dev/guides/libraries/private-files#pubspeclock.
22+
pubspec.lock
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## 0.1.0
2+
3+
- Initial version.

shelf_packages/shelf_routing/LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024 BreX900
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../shelf_routing_generator/README.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# This file configures the static analysis results for your project (errors,
2+
# warnings, and lints).
3+
#
4+
# This enables the 'recommended' set of lints from `package:lints`.
5+
# This set helps identify many issues that may lead to problems when running
6+
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
7+
# style and format.
8+
#
9+
# If you want a smaller set of lints you can change this to specify
10+
# 'package:lints/core.yaml'. These are just the most critical lints
11+
# (the recommended set includes the core lints).
12+
# The core lints are also what is used by pub.dev for scoring packages.
13+
14+
include: package:mek_lints/dart_package.yaml
15+
16+
# Uncomment the following section to specify additional rules.
17+
18+
# linter:
19+
# rules:
20+
# - camel_case_types
21+
22+
analyzer:
23+
exclude:
24+
- example/**
25+
26+
# For more information about the core and recommended set of lints, see
27+
# https://dart.dev/go/core-lints
28+
29+
# For additional information about configuring this file, see
30+
# https://dart.dev/guides/language/analysis-options
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/// See example in 'shelf_routing_generator' package.
2+
/// https://pub.dev/packages/shelf_routing_generator/example
3+
void main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
library shelf_routing;
2+
3+
import 'package:meta/meta_meta.dart';
4+
5+
export 'package:shelf_routing/src/get_request_extension.dart';
6+
export 'package:shelf_routing/src/json_response.dart';
7+
export 'package:shelf_routing/src/utils.dart';
8+
9+
/// Annotation for top level getters to generate a router that handles all routers of the types
10+
/// passed using the package `shelf_routing_generator`.
11+
///
12+
/// **Example**
13+
/// ```dart
14+
/// // Always import 'shelf_router' without 'show' or 'as'.
15+
/// import 'package:shelf_router/shelf_router.dart';
16+
/// // Always import 'shelf_routing' without 'show' or 'as'.
17+
/// import 'package:shelf_routing/shelf_routing.dart' show Request, Response;
18+
///
19+
/// // Include generated code, this assumes current file is 'services_router.dart'.
20+
/// part 'services_router.g.dart';
21+
///
22+
/// // Get a router for all these services.
23+
/// @GenerateRouterFor([MyService, MyAnotherService])
24+
/// Router get router => _$router;
25+
/// ```
26+
@Target({TargetKind.getter})
27+
class GenerateRouterFor {
28+
/// List of services that have a static getter that returns a Router
29+
final List<Type> routables;
30+
31+
/// See class documentation
32+
const GenerateRouterFor(this.routables);
33+
}
34+
35+
/// Annotation for services using on methods the Route annotation to add a [prefix] when the router
36+
/// is mounted via the [GenerateRouterFor] annotation using the package `shelf_routing_generator`.
37+
///
38+
/// **Example**
39+
/// ```dart
40+
/// // Always import 'shelf_router' without 'show' or 'as'.
41+
/// import 'package:shelf_router/shelf_router.dart';
42+
/// // Always import 'shelf_routing' without 'show' or 'as'.
43+
/// import 'package:shelf_routing/shelf_routing.dart' show Request, Response;
44+
///
45+
/// // Include generated code, this assumes current file is 'service.dart'.
46+
/// part 'service.g.dart';
47+
///
48+
/// // Get a router for all these services.
49+
/// @Routable(prefix: '/say-hello')
50+
/// class Service {
51+
/// @Route.get('/<name>')
52+
/// Future<Response> _sayHello(Request request, String name) async {
53+
/// return Response.ok('hello $name');
54+
/// }
55+
///
56+
/// /// Get a router for this service.
57+
/// Router get router => _$ServiceRouter(this);
58+
/// }
59+
/// ```
60+
@Target({TargetKind.classType})
61+
class Routable {
62+
/// Router prefix
63+
final String prefix;
64+
65+
/// See class documentation
66+
const Routable({required this.prefix});
67+
}
68+
69+
/// Annotation for methods using Route annotation to indicate that the route should be called with
70+
/// this the header [name] using the package `shelf_routing_generator`.
71+
///
72+
/// **Example**
73+
/// ```dart
74+
/// import 'package:shelf/shelf.dart';
75+
/// // Always import 'shelf_routing' without 'show' or 'as'.
76+
/// import 'package:shelf_routing/shelf_routing.dart' show Request, Response;
77+
///
78+
/// // Include generated code, this assumes current file is 'service.dart'.
79+
/// part 'service.g.dart';
80+
///
81+
/// class Service {
82+
/// @Route.get('/<name>')
83+
/// @RouteHeader(name: 'authentication')
84+
/// Future<Response> _sayHello(Request request, String name) async {
85+
/// return Response.ok('hello $name');
86+
/// }
87+
/// }
88+
/// ```
89+
@Target({TargetKind.method})
90+
class RouteHeader {
91+
/// The header name
92+
final String name;
93+
94+
/// See class documentation
95+
const RouteHeader({required this.name});
96+
}
97+
98+
/// Identifies the data type of the request.
99+
enum BadRequestPosition {
100+
/// Request header
101+
header,
102+
103+
/// Path parameter
104+
path,
105+
106+
/// Query parameter
107+
queryParameter,
108+
109+
/// Request body
110+
body
111+
}
112+
113+
/// The exception that will be thrown in case the validation/parsing of the request fails.
114+
class BadRequestException implements Exception {
115+
/// Identifies the type of invalid data.
116+
final BadRequestPosition position;
117+
118+
/// The error generated by validation/parsing.
119+
final Object error;
120+
121+
/// The stack trace generated by validation/parsing.
122+
final StackTrace stackTrace;
123+
124+
/// The invalid field name.
125+
final String? name;
126+
127+
/// Constructor for a request header error.
128+
BadRequestException.header(
129+
this.error,
130+
this.stackTrace,
131+
String this.name,
132+
) : position = BadRequestPosition.header;
133+
134+
/// Constructor for a request path parameter error.
135+
BadRequestException.path(this.error, this.stackTrace)
136+
: position = BadRequestPosition.path,
137+
name = null;
138+
139+
/// Constructor for a request query parameter error.
140+
BadRequestException.queryParameter(this.error, this.stackTrace, String this.name)
141+
: position = BadRequestPosition.queryParameter;
142+
143+
/// Constructor for a request body error.
144+
BadRequestException.body(this.error, this.stackTrace)
145+
: position = BadRequestPosition.body,
146+
name = null;
147+
148+
@override
149+
String toString() => 'Missing or invalid ${position.name}${name != null ? ' "$name"' : ''}.';
150+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/// Specifies the array format (a single parameter with multiple parameter
2+
/// or multiple parameters with the same name).
3+
/// and the separator for array items.
4+
enum QueryParameterFormat {
5+
/// Comma-separated values.
6+
/// e.g. (foo,bar,baz)
7+
// csv,
8+
form,
9+
10+
/// Space-separated values.
11+
/// e.g. (foo bar baz)
12+
// ssv,
13+
spaceDelimited,
14+
15+
/// Pipe-separated values.
16+
/// e.g. (foo|bar|baz)
17+
// pipes,
18+
pipeDelimited,
19+
20+
/// Multiple parameter instances rather than multiple values.
21+
/// e.g. (foo=value&foo=another_value)
22+
// multi,
23+
formExpanded;
24+
}
25+
26+
/// Specifies the array format (a single parameter with multiple parameter
27+
/// or multiple parameters with the same name).
28+
/// and the separator for array items.
29+
enum HeaderFormat {
30+
/// Comma-separated values.
31+
/// e.g. (foo,bar,baz)
32+
// csv,
33+
simple,
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import 'package:shelf/shelf.dart';
2+
3+
/// Getter definition for dependency injection.
4+
typedef RequestGetter = T Function<T extends Object>(Request request);
5+
6+
/// Dependency injection extensions.
7+
extension GetterRequestExtensions on Request {
8+
static const String _key = '_getter';
9+
10+
/// Retrieve data using the getter injected into the request via the [GetterMiddleware] middleware.
11+
T get<T extends Object>() {
12+
final getter = context[_key] as RequestGetter?;
13+
14+
assert(getter != null,
15+
'Missing getter scope in request context.\nUse getterScope method to provider it.');
16+
17+
return getter!<T>(this);
18+
}
19+
20+
/// Change the dependency injection [getter].
21+
Request changeGetter(RequestGetter getter) => change(context: {_key: getter});
22+
}
23+
24+
/// [Middleware] to inject [_getter] to retrieve data by dependency injection.
25+
class GetterMiddleware {
26+
final RequestGetter _getter;
27+
28+
/// See class documentation
29+
const GetterMiddleware(this._getter);
30+
31+
/// [Middleware] method
32+
Handler call(Handler innerHandler) {
33+
return (request) {
34+
// ignore: discarded_futures
35+
return innerHandler(request.changeGetter(_getter));
36+
};
37+
}
38+
}

0 commit comments

Comments
 (0)