Skip to content

Commit

Permalink
Merge branch 'master' into 0.7.0
Browse files Browse the repository at this point in the history
  • Loading branch information
RicardoRB committed Jun 19, 2024
2 parents 56f8ede + b9ac2e9 commit 3c5ce87
Show file tree
Hide file tree
Showing 17 changed files with 400 additions and 19 deletions.
2 changes: 2 additions & 0 deletions melos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ scripts:

test: melos exec --dir-exists=test --no-private -- dart test

pub:upgrade: melos exec --no-flutter -- dart pub upgrade

doc: melos exec --no-private -- dart doc .

publish:force: dart pub publish -f
6 changes: 5 additions & 1 deletion packages/dartness_generator/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,14 @@
- Fixed error with topological sort when using `useFactory`
- Controller path variable is optional

## 0.6.1

- Updated dartness_server version

## 0.7.0

- Added schedulers

## 0.7.1

- Fixed Dartness server version package
- Fixed Dartness server version package
7 changes: 6 additions & 1 deletion packages/dartness_server/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@

- Added https exceptions


## 0.6.1

- Fixed error with QueryParam as a List

## 0.7.0

- Added schedulers
- Added schedulers
4 changes: 2 additions & 2 deletions packages/dartness_server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ $ dart create -t console your_project_name

```yaml
dependencies:
dartness_server: ^0.6.0
dartness_server: ^0.6.1

dev_dependencies:
build_runner: ^2.2.0
dartness_generator: ^0.6.0
dartness_generator: ^0.6.1
```
2. Create the file in "bin/main.dart"
Expand Down
16 changes: 16 additions & 0 deletions packages/dartness_server/lib/exception.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,19 @@ export 'src/exception/dartness_catch_handler.dart';
export 'src/exception/dartness_error_handler.dart';
export 'src/exception/dartness_error_handler_register.dart';
export 'src/exception/http_status_code_exception.dart';
export 'src/exception/bad_gateway_exception.dart';
export 'src/exception/bad_request_exception.dart';
export 'src/exception/conflict_exception.dart';
export 'src/exception/forbidden_exception.dart';
export 'src/exception/gateway_timeout_exception.dart';
export 'src/exception/gone_exception.dart';
export 'src/exception/internal_server_error_exception.dart';
export 'src/exception/method_not_allowed_exception.dart';
export 'src/exception/not_acceptable_exception.dart';
export 'src/exception/not_found_exception.dart';
export 'src/exception/not_implemented_exception.dart';
export 'src/exception/service_unavailable_exception.dart';
export 'src/exception/too_many_requests_exception.dart';
export 'src/exception/unauthorized_exception.dart';
export 'src/exception/unprocessable_entity_exception.dart';
export 'src/exception/unsupported_media_type_exception.dart';
1 change: 1 addition & 0 deletions packages/dartness_server/lib/schedule.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export 'src/configuration/schedule/scheduled.dart';
export 'src/configuration/schedule/scheduler.dart';
export 'src/configuration/schedule/time_unit.dart';
export 'src/configuration/schedule/scheduler_manager.dart';

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/// A metadata annotation used to mark a class as available to the dependency injection system.
///
/// This annotation can be applied to classes which should be instantiated or managed by the DI framework.
///
/// Marking a class with `@Injectable` indicates to the DI framework that an instance of the class
/// can be created and provided to other parts of the application that require it.
///
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/// A class representing a cron expression, used for scheduling tasks.
class CronExpression {
final List<String> seconds;
final List<String> minutes;
final List<String> hours;
final List<String> days;
final List<String> months;
final List<String> weekdays;

CronExpression({
required this.seconds,
required this.minutes,
required this.hours,
required this.days,
required this.months,
required this.weekdays,
});

/// Parses a cron string into its components.
///
/// The cron string must have 6 parts, separated by spaces, representing:
/// seconds, minutes, hours, day of month, month, and day of week.
factory CronExpression.parse(String cronString) {
final parts = cronString.split(' ');
if (parts.length != 6) {
throw FormatException('Invalid cron string length');
}

// Validate and parse each field
return CronExpression(
seconds: _parseField(parts[0], 0, 59),
minutes: _parseField(parts[1], 0, 59),
hours: _parseField(parts[2], 0, 23),
days: _parseField(parts[3], 1, 31),
months: _parseField(parts[4], 1, 12),
weekdays: _parseField(parts[5], 0, 7),
);
}

static List<String> _parseField(String field, int min, int max) {
// Wildcard
if (field == '*') {
return [field];
}

// Step values
if (field.contains('/')) {
final stepParts = field.split('/');
if (stepParts[0] != '*' &&
!stepParts[0].contains('-') &&
int.tryParse(stepParts[1]) == null) {
throw FormatException('Unsupported or malformed step value');
}
if (stepParts[0] == '*' && int.parse(stepParts[1]) > max) {
throw FormatException('Step value out of range');
}
return [field];
}

// Range values
if (field.contains('-')) {
final rangeParts = field.split('-').map(int.parse).toList();
if (rangeParts[0] > rangeParts[1]) {
throw FormatException('Invalid range');
}
if (rangeParts.any((val) => val < min || val > max)) {
throw FormatException('Range value out of bounds');
}
return [field];
}

// List values
final listParts = field.split(',');
for (var part in listParts) {
final value = int.tryParse(part);
if (value == null || value < min || value > max) {
throw FormatException('Value out of bounds');
}
}
return listParts;
}

@override
String toString() {
return 'CronExpression(seconds: $seconds, minutes: $minutes, hours: $hours, days: $days, months: $months, weekdays: $weekdays)';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import 'dart:async';

import 'cron_expression.dart';
import 'system_time_provider.dart';
import 'time_provider.dart';

/// Scheduler class that uses a [CronExpression] to execute tasks periodically.
class CronScheduler {
final CronExpression cronExpression;
// TimeProvider allows for injecting a custom time source, facilitating testing.
final TimeProvider timeProvider;
Timer? _timer;

// Constructor accepts a cron expression and an optional custom TimeProvider.
// If no TimeProvider is provided, it defaults to the system clock.
CronScheduler(this.cronExpression, {TimeProvider? timeProvider})
: timeProvider = timeProvider ?? SystemTimeProvider();

/// Starts the periodic execution of a task based on the cron expression.
/// [task] is a function with no parameters that encapsulates
/// the invocation of the actual task function with its parameters.
void start(void Function() task) {
_timer = Timer.periodic(Duration(seconds: 1), (Timer t) {
var now = timeProvider.now();
// Checks if the current time matches the cron expression before executing the task.
if (_matches(now)) {
task();
}
});
}

/// Checks if the current time matches the cron expression.
bool _matches(final DateTime dateTime) {
return (cronExpression.seconds.contains('*') ||
cronExpression.seconds.contains(dateTime.second.toString())) &&
(cronExpression.minutes.contains('*') ||
cronExpression.minutes.contains(dateTime.minute.toString())) &&
(cronExpression.hours.contains('*') ||
cronExpression.hours.contains(dateTime.hour.toString())) &&
(cronExpression.days.contains('*') ||
cronExpression.days.contains(dateTime.day.toString())) &&
(cronExpression.months.contains('*') ||
cronExpression.months.contains(dateTime.month.toString())) &&
(cronExpression.weekdays.contains('*') ||
cronExpression.weekdays.contains(dateTime.weekday.toString()));
}

/// Stops the periodic execution of the task.
void stop() {
_timer?.cancel();
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
/// Represents an annotation for scheduling task
import 'package:dartness_server/src/configuration/schedule/time_unit.dart';

/// Represents an annotation for scheduling tasks with various parameters.
class Scheduled {
/// A cron-like expression defining the triggers for the scheduled method.
final String cron;
final String? cron;

/// Constructor for the Scheduled annotation.
const Scheduled({
required this.cron,
this.cron,
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import 'time_provider.dart';

// Default implementation of TimeProvider using the system clock.
class SystemTimeProvider implements TimeProvider {
@override
DateTime now() => DateTime.now();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Abstract class defining the interface for a time provider.
abstract class TimeProvider {
// Method that should return the current time.
DateTime now();
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import 'package:dartness_server/src/exception/http_client_error_exception.dart';
/// that the server understands the content type of the request entity but was
/// unable to process the contained instructions. It includes a human-readable
/// [message] and has an HTTP status code of [HttpStatus.unprocessableEntity].
class UnprocessableEntityException extends HttpClientErrorException {
/// Creates an [UnprocessableEntityException] with the specified [message].
class UnsupportedMediaTypeException extends HttpClientErrorException {
/// Creates an [UnsupportedMediaTypeException] with the specified [message].
///
/// The [message] is a human-readable description of the unprocessable entity error.
const UnprocessableEntityException(String message)
const UnsupportedMediaTypeException(String message)
: super(message, HttpStatus.unprocessableEntity);
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,20 @@ class DartnessRouterHandler {
} else {
if (param.isPath) {
final pathParam = _getPathParam(request, param);
final value = pathParam.stringToType(param.type);
namedArguments[Symbol(param.name)] = value;
if (pathParam is String) {
final value = pathParam.stringToType(param.type);
namedArguments[Symbol(param.name)] = value;
} else {
namedArguments[Symbol(param.name)] = pathParam;
}
} else {
final String? queryParam = _getQueryParam(request, param);
final value = queryParam?.stringToType(param.type);
namedArguments[Symbol(param.name)] = value;
final Object? queryParam = _getQueryParam(request, param);
if (queryParam is String) {
final value = queryParam.stringToType(param.type);
namedArguments[Symbol(param.name)] = value;
} else {
namedArguments[Symbol(param.name)] = queryParam;
}
}
}
}
Expand Down Expand Up @@ -87,9 +95,15 @@ class DartnessRouterHandler {
);
}

dynamic _getQueryParam(Request request, DartnessParam param) =>
request.url.queryParameters[param.name] ?? param.defaultValue;
Object? _getQueryParam(final Request request, final DartnessParam param) {
final all = request.url.queryParametersAll;
final foundAll = all[param.name];
if (foundAll != null && foundAll.length > 1) {
return all[param.name];
}
return request.url.queryParameters[param.name] ?? param.defaultValue;
}

dynamic _getPathParam(Request request, DartnessParam param) =>
Object? _getPathParam(final Request request, final DartnessParam param) =>
request.params[param.name] ?? param.defaultValue;
}
2 changes: 1 addition & 1 deletion packages/dartness_server/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ dev_dependencies:
http: ^1.2.1
lints: ^4.0.0
test: ^1.25.7
build_runner: ^2.4.11
build_runner: ^2.4.11
Loading

0 comments on commit 3c5ce87

Please sign in to comment.