add:(laravel): adding support for laravel inspired serviceproviders
This commit is contained in:
parent
1a5489a993
commit
a8da6b8600
18 changed files with 3923 additions and 77 deletions
|
@ -1,86 +1,32 @@
|
|||
# Protevus Framework
|
||||
# Protevus Platform Core
|
||||
|
||||
[![Protevus Framework](../../angel3_logo.png)](https://github.com/dart-backend/angel)
|
||||
<p align="center"><img src="https://raw.githubusercontent.com/protevus/branding/main/protevus-logo-bg.png" alt="Protevus Logo" width="400"></p>
|
||||
|
||||
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/angel3_framework?include_prereleases)
|
||||
[![Pub Version (including pre-releases)](https://img.shields.io/pub/v/protevus_core?include_prereleases)](https://pub.dev/packages/protevus_core)
|
||||
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
|
||||
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
|
||||
[![License](https://img.shields.io/github/license/dart-backend/angel)](https://github.com/dart-backend/angel/tree/master/packages/framework/LICENSE)
|
||||
[![melos](https://img.shields.io/badge/maintained%20with-melos-f700ff.svg?style=flat-square)](https://github.com/invertase/melos)
|
||||
[![License](https://img.shields.io/github/license/protevus/platform)](https://github.com/protevus/platform/blob/main/packages/core/LICENSE)
|
||||
|
||||
Protevus framework is a high-powered HTTP server with support for dependency injection, sophisticated routing, authentication, ORM, graphql etc. It is designed to keep the core minimal but extensible through a series of plugin packages. It won't dictate which features, databases or web templating engine to use. This flexibility enable Protevus framework to grow with your application as new features can be added to handle the new use cases.
|
||||
Protevus Platform Core is a high-powered HTTP server framework for Dart, designed to be minimal yet extensible. It provides a robust foundation for building scalable and flexible web applications.
|
||||
|
||||
This package is the core package of [Protevus](https://github.com/dart-backend/angel). For more information, visit us at [Protevus Website](https://angel3-framework.web.app).
|
||||
## Features
|
||||
|
||||
## Installation and Setup
|
||||
- Dependency injection
|
||||
- Sophisticated routing
|
||||
- Authentication support
|
||||
- ORM integration
|
||||
- GraphQL support
|
||||
- Extensible plugin architecture
|
||||
- HTTP/2 support
|
||||
- WebSocket capabilities
|
||||
- File handling and MIME type detection
|
||||
- Environment configuration management
|
||||
- Performance optimization with caching mechanisms
|
||||
|
||||
### (Option 1) Create a new project by cloning from boilerplate templates
|
||||
## Installation
|
||||
|
||||
1. Download and install [Dart](https://dart.dev/get-dart)
|
||||
Add `protevus_core` to your `pubspec.yaml`:
|
||||
|
||||
2. Clone one of the following starter projects:
|
||||
* [Protevus Basic Template](https://github.com/dukefirehawk/boilerplates/tree/v7/angel3-basic)
|
||||
* [Protevus ORM Template](https://github.com/dukefirehawk/boilerplates/tree/v7/angel3-orm)
|
||||
* [Protevus ORM MySQL Template](https://github.com/dukefirehawk/boilerplates/tree/v7/angel3-orm-mysql)
|
||||
* [Protevus Graphql Template](https://github.com/dukefirehawk/boilerplates/tree/v7/angel3-graphql)
|
||||
|
||||
3. Run the project in development mode (*hot-reloaded* is enabled on file changes).
|
||||
|
||||
```bash
|
||||
dart --observe bin/dev.dart
|
||||
```
|
||||
|
||||
4. Run the project in production mode (*hot-reloaded* is disabled).
|
||||
|
||||
```bash
|
||||
dart bin/prod.dart
|
||||
```
|
||||
|
||||
5. Run as docker. Edit and build the image with the provided `Dockerfile` file.
|
||||
|
||||
### (Option 2) Create a new project with Protevus CLI
|
||||
|
||||
1. Download and install [Dart](https://dart.dev/get-dart)
|
||||
|
||||
2. Install the [Protevus CLI](https://pub.dev/packages/angel3_cli):
|
||||
|
||||
```bash
|
||||
dart pub global activate angel3_cli
|
||||
```
|
||||
|
||||
3. On terminal, create a new project:
|
||||
|
||||
```bash
|
||||
angel3 init hello
|
||||
```
|
||||
|
||||
4. Run the project in development mode (*hot-reloaded* is enabled on file changes).
|
||||
|
||||
```bash
|
||||
dart --observe bin/dev.dart
|
||||
```
|
||||
|
||||
5. Run the project in production mode (*hot-reloaded* is disabled).
|
||||
|
||||
```bash
|
||||
dart bin/prod.dart
|
||||
```
|
||||
|
||||
6. Run as docker. Edit and build the image with the provided `Dockerfile` file.
|
||||
|
||||
## Performance Benchmark
|
||||
|
||||
The performance benchmark can be found at
|
||||
|
||||
[TechEmpower Framework Benchmarks Round 21](https://www.techempower.com/benchmarks/#section=data-r21&test=composite)
|
||||
|
||||
### Migrating from Angel to Protevus
|
||||
|
||||
Check out [Migrating to Protevus](https://angel3-docs.dukefirehawk.com/migration/angel-2.x.x-to-angel3/migration-guide-3)
|
||||
|
||||
## Donation & Support
|
||||
|
||||
If you like this project and interested in supporting its development, you can make a donation using the following services:
|
||||
|
||||
* [![GitHub](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86)](https://github.com/sponsors/dukefirehawk)
|
||||
* [paypal](https://paypal.me/dukefirehawk?country.x=MY&locale.x=en_US) service
|
||||
```yaml
|
||||
dependencies:
|
||||
protevus_core: ^latest_version
|
||||
```
|
3
packages/core/lib/providers.dart
Normal file
3
packages/core/lib/providers.dart
Normal file
|
@ -0,0 +1,3 @@
|
|||
export 'src/provider/platform_with_providers.dart';
|
||||
export 'src/provider/service_provider.dart';
|
||||
export 'src/provider/service_provider_config.dart';
|
93
packages/core/lib/src/provider/platform_with_providers.dart
Normal file
93
packages/core/lib/src/provider/platform_with_providers.dart
Normal file
|
@ -0,0 +1,93 @@
|
|||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_container/container.dart';
|
||||
import 'package:platform_container/mirrors.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'service_provider.dart';
|
||||
import 'service_provider_manager.dart';
|
||||
import 'service_provider_config.dart';
|
||||
import 'provider_discovery.dart';
|
||||
|
||||
class PlatformWithProviders {
|
||||
final Application app;
|
||||
late ServiceProviderManager _serviceProviderManager;
|
||||
late ProviderDiscovery _discovery;
|
||||
late Container container;
|
||||
|
||||
PlatformWithProviders(this.app,
|
||||
{ServiceProviderManager? serviceProviderManager}) {
|
||||
container = Container(MirrorsReflector());
|
||||
app.configure(configureContainer);
|
||||
|
||||
_serviceProviderManager =
|
||||
serviceProviderManager ?? ServiceProviderManager(app, _discovery);
|
||||
}
|
||||
|
||||
Future<void> configureContainer(Application app) async {
|
||||
// Register the container itself
|
||||
container.registerSingleton((c) => container);
|
||||
|
||||
// Configure the container here
|
||||
// For example, you might want to register some services:
|
||||
// container.registerSingleton((c) => SomeService());
|
||||
|
||||
// You might need to set up dependency injection for routes here
|
||||
// app.use((req, res) => container.make<SomeService>().handleRequest(req, res));
|
||||
}
|
||||
|
||||
Future<void> registerProvidersFromConfig(ServiceProviderConfig config) async {
|
||||
for (var providerType in config.providers) {
|
||||
var provider = _discovery.createInstance(providerType);
|
||||
if (provider != null) {
|
||||
if (config.deferredProviders.containsKey(providerType)) {
|
||||
provider.setDeferred(config.deferredProviders[providerType]!);
|
||||
}
|
||||
if (config.providerConfigs.containsKey(providerType)) {
|
||||
provider.configure(config.providerConfigs[providerType]!);
|
||||
}
|
||||
await _serviceProviderManager.register(provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> registerServiceProvider(ServiceProvider provider) async {
|
||||
await _serviceProviderManager.register(provider);
|
||||
}
|
||||
|
||||
void unregisterServiceProvider(Type providerType) {
|
||||
_serviceProviderManager.unregister(providerType);
|
||||
}
|
||||
|
||||
T? getServiceProvider<T extends ServiceProvider>() {
|
||||
return _serviceProviderManager.getProvider<T>();
|
||||
}
|
||||
|
||||
Future<void> bootServiceProviders() async {
|
||||
await _serviceProviderManager.bootAll();
|
||||
}
|
||||
|
||||
Future<void> bootDeferredServiceProvider(Type providerType) async {
|
||||
await _serviceProviderManager.bootDeferredProvider(providerType);
|
||||
}
|
||||
|
||||
void setServiceProviderLogLevel(Level level) {
|
||||
_serviceProviderManager.setLogLevel(level);
|
||||
}
|
||||
|
||||
Future<void> discoverServiceProviders() async {
|
||||
await _serviceProviderManager.discoverProviders();
|
||||
}
|
||||
|
||||
void registerProviderType(Type type, ServiceProvider Function() factory) {
|
||||
if (_discovery is ManualProviderDiscovery) {
|
||||
(_discovery as ManualProviderDiscovery)
|
||||
.registerProviderType(type, factory);
|
||||
} else {
|
||||
throw UnsupportedError(
|
||||
'Provider type registration is only supported with ManualProviderDiscovery');
|
||||
}
|
||||
}
|
||||
|
||||
T? getService<T>() {
|
||||
return app.container.make<T>();
|
||||
}
|
||||
}
|
54
packages/core/lib/src/provider/provider_discovery.dart
Normal file
54
packages/core/lib/src/provider/provider_discovery.dart
Normal file
|
@ -0,0 +1,54 @@
|
|||
import 'dart:mirrors' as mirrors;
|
||||
import 'service_provider.dart';
|
||||
|
||||
abstract class ProviderDiscovery {
|
||||
List<Type> discoverProviders();
|
||||
ServiceProvider? createInstance(Type type);
|
||||
}
|
||||
|
||||
class MirrorProviderDiscovery implements ProviderDiscovery {
|
||||
@override
|
||||
List<Type> discoverProviders() {
|
||||
var providers = <Type>[];
|
||||
mirrors.currentMirrorSystem().libraries.values.forEach((lib) {
|
||||
for (var declaration in lib.declarations.values) {
|
||||
if (declaration is mirrors.ClassMirror &&
|
||||
declaration.isSubclassOf(mirrors.reflectClass(ServiceProvider)) &&
|
||||
!declaration.isAbstract) {
|
||||
providers.add(declaration.reflectedType);
|
||||
}
|
||||
}
|
||||
});
|
||||
return providers;
|
||||
}
|
||||
|
||||
@override
|
||||
ServiceProvider? createInstance(Type type) {
|
||||
var classMirror = mirrors.reflectClass(type);
|
||||
if (classMirror.declarations.containsKey(const Symbol(''))) {
|
||||
var ctor =
|
||||
classMirror.declarations[const Symbol('')] as mirrors.MethodMirror?;
|
||||
if (ctor != null && ctor.isConstructor && ctor.parameters.isEmpty) {
|
||||
return classMirror.newInstance(const Symbol(''), []).reflectee
|
||||
as ServiceProvider;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class ManualProviderDiscovery implements ProviderDiscovery {
|
||||
final List<Type> _registeredTypes = [];
|
||||
final Map<Type, ServiceProvider Function()> _factories = {};
|
||||
|
||||
void registerProviderType(Type type, ServiceProvider Function() factory) {
|
||||
_registeredTypes.add(type);
|
||||
_factories[type] = factory;
|
||||
}
|
||||
|
||||
@override
|
||||
List<Type> discoverProviders() => List.unmodifiable(_registeredTypes);
|
||||
|
||||
@override
|
||||
ServiceProvider? createInstance(Type type) => _factories[type]?.call();
|
||||
}
|
39
packages/core/lib/src/provider/service_provider.dart
Normal file
39
packages/core/lib/src/provider/service_provider.dart
Normal file
|
@ -0,0 +1,39 @@
|
|||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_container/container.dart';
|
||||
|
||||
abstract class ServiceProvider {
|
||||
late Application app;
|
||||
late Container container;
|
||||
late final Map<String, dynamic> _config;
|
||||
bool _isEnabled = true;
|
||||
bool _isDeferred = false;
|
||||
|
||||
ServiceProvider([Map<String, dynamic>? config]) : _config = config ?? {};
|
||||
|
||||
Map<String, dynamic> get config => _config;
|
||||
bool get isEnabled => _isEnabled;
|
||||
bool get isDeferred => _isDeferred;
|
||||
|
||||
void configure(Map<String, dynamic> options) {
|
||||
_config.addAll(options);
|
||||
}
|
||||
|
||||
void setEnabled(bool enabled) {
|
||||
_isEnabled = enabled;
|
||||
}
|
||||
|
||||
void setDeferred(bool deferred) {
|
||||
_isDeferred = deferred;
|
||||
}
|
||||
|
||||
void registerWithContainer(Container container) {
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
Future<void> register();
|
||||
Future<void> beforeBoot() async {}
|
||||
Future<void> boot();
|
||||
Future<void> afterBoot() async {}
|
||||
|
||||
List<Type> get dependencies => [];
|
||||
}
|
82
packages/core/lib/src/provider/service_provider_config.dart
Normal file
82
packages/core/lib/src/provider/service_provider_config.dart
Normal file
|
@ -0,0 +1,82 @@
|
|||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/src/provider/service_provider.dart';
|
||||
|
||||
class ServiceProviderConfig {
|
||||
final List<Type> providers;
|
||||
final Map<Type, bool> deferredProviders;
|
||||
final Map<Type, Map<String, dynamic>> providerConfigs;
|
||||
|
||||
ServiceProviderConfig({
|
||||
required this.providers,
|
||||
this.deferredProviders = const {},
|
||||
this.providerConfigs = const {},
|
||||
});
|
||||
|
||||
factory ServiceProviderConfig.fromMap(Map<String, dynamic> map) {
|
||||
final providersList =
|
||||
(map['providers'] as List<dynamic>).cast<Map<String, dynamic>>();
|
||||
|
||||
final providers = <Type>[];
|
||||
final deferredProviders = <Type, bool>{};
|
||||
final providerConfigs = <Type, Map<String, dynamic>>{};
|
||||
|
||||
for (var providerMap in providersList) {
|
||||
final type = _getTypeFromString(providerMap['type'] as String);
|
||||
providers.add(type);
|
||||
|
||||
if (providerMap['deferred'] == true) {
|
||||
deferredProviders[type] = true;
|
||||
}
|
||||
|
||||
if (providerMap['config'] != null) {
|
||||
providerConfigs[type] = (providerMap['config'] as Map<String, dynamic>);
|
||||
}
|
||||
}
|
||||
|
||||
return ServiceProviderConfig(
|
||||
providers: providers,
|
||||
deferredProviders: deferredProviders,
|
||||
providerConfigs: providerConfigs,
|
||||
);
|
||||
}
|
||||
|
||||
static Type _getTypeFromString(String typeName) {
|
||||
// This is a simple implementation. You might need to expand this
|
||||
// to cover all your service provider types.
|
||||
switch (typeName) {
|
||||
case 'TestServiceProvider':
|
||||
return TestServiceProvider;
|
||||
case 'AnotherServiceProvider':
|
||||
return AnotherServiceProvider;
|
||||
// Add cases for all your service provider types
|
||||
default:
|
||||
throw UnimplementedError(
|
||||
'Type $typeName is not implemented in _getTypeFromString');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Example service provider classes (you should replace these with your actual service providers)
|
||||
class TestServiceProvider extends ServiceProvider {
|
||||
@override
|
||||
Future<void> register() async {
|
||||
// Implementation
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> boot() async {
|
||||
// Implementation
|
||||
}
|
||||
}
|
||||
|
||||
class AnotherServiceProvider extends ServiceProvider {
|
||||
@override
|
||||
Future<void> register() async {
|
||||
// Implementation
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> boot() async {
|
||||
// Implementation
|
||||
}
|
||||
}
|
131
packages/core/lib/src/provider/service_provider_manager.dart
Normal file
131
packages/core/lib/src/provider/service_provider_manager.dart
Normal file
|
@ -0,0 +1,131 @@
|
|||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_container/container.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'service_provider.dart';
|
||||
import 'provider_discovery.dart';
|
||||
|
||||
class ServiceProviderManager {
|
||||
final Application app;
|
||||
final Container container;
|
||||
final Map<Type, ServiceProvider> _providers = {};
|
||||
final Set<Type> _booted = {};
|
||||
final Logger _logger;
|
||||
final ProviderDiscovery _discovery;
|
||||
|
||||
ServiceProviderManager(this.app, this._discovery)
|
||||
: container = app.container,
|
||||
_logger = Logger('ServiceProviderManager');
|
||||
|
||||
void setLogLevel(Level level) {
|
||||
_logger.level = level;
|
||||
}
|
||||
|
||||
Future<void> register(ServiceProvider provider) async {
|
||||
provider.app = app;
|
||||
provider.registerWithContainer(container);
|
||||
_providers[provider.runtimeType] = provider;
|
||||
try {
|
||||
_logger.info('Registering ${provider.runtimeType}');
|
||||
await provider.register();
|
||||
_logger.info('Registered ${provider.runtimeType}');
|
||||
} catch (e) {
|
||||
_logger.severe('Failed to register ${provider.runtimeType}', e);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
void unregister(Type providerType) {
|
||||
if (_providers.containsKey(providerType)) {
|
||||
_logger.info('Unregistering $providerType');
|
||||
_providers.remove(providerType);
|
||||
_booted.remove(providerType);
|
||||
} else {
|
||||
_logger.warning(
|
||||
'Attempted to unregister non-existent provider: $providerType');
|
||||
}
|
||||
}
|
||||
|
||||
T? getProvider<T extends ServiceProvider>() {
|
||||
return _providers[T] as T?;
|
||||
}
|
||||
|
||||
Future<void> bootAll() async {
|
||||
for (var providerType in _providers.keys) {
|
||||
await _bootProvider(providerType);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _bootProvider(Type providerType) async {
|
||||
if (_booted.contains(providerType)) return;
|
||||
|
||||
var provider = _providers[providerType];
|
||||
if (provider == null) {
|
||||
_logger.severe('Provider not found: $providerType');
|
||||
throw ProviderNotFoundException(providerType);
|
||||
}
|
||||
|
||||
if (!provider.isEnabled) {
|
||||
_logger.info('Skipping disabled provider: $providerType');
|
||||
return;
|
||||
}
|
||||
|
||||
if (provider.isDeferred) {
|
||||
_logger.info('Skipping deferred provider: $providerType');
|
||||
return;
|
||||
}
|
||||
|
||||
for (var dependencyType in provider.dependencies) {
|
||||
await _bootProvider(dependencyType);
|
||||
}
|
||||
|
||||
try {
|
||||
_logger.info('Booting ${provider.runtimeType}');
|
||||
await provider.beforeBoot();
|
||||
await provider.boot();
|
||||
await provider.afterBoot();
|
||||
_booted.add(providerType);
|
||||
_logger.info('Booted ${provider.runtimeType}');
|
||||
} catch (e) {
|
||||
_logger.severe('Failed to boot ${provider.runtimeType}', e);
|
||||
throw ProviderBootException(provider, e);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> bootDeferredProvider(Type providerType) async {
|
||||
var provider = _providers[providerType];
|
||||
if (provider == null || !provider.isDeferred) {
|
||||
throw ProviderNotFoundException(providerType);
|
||||
}
|
||||
await _bootProvider(providerType);
|
||||
}
|
||||
|
||||
Future<void> discoverProviders() async {
|
||||
for (var type in _discovery.discoverProviders()) {
|
||||
if (!_providers.containsKey(type)) {
|
||||
var instance = _discovery.createInstance(type);
|
||||
if (instance != null) {
|
||||
await register(instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ProviderNotFoundException implements Exception {
|
||||
final Type providerType;
|
||||
|
||||
ProviderNotFoundException(this.providerType);
|
||||
|
||||
@override
|
||||
String toString() => 'Provider not found: $providerType';
|
||||
}
|
||||
|
||||
class ProviderBootException implements Exception {
|
||||
final ServiceProvider provider;
|
||||
final Object error;
|
||||
|
||||
ProviderBootException(this.provider, this.error);
|
||||
|
||||
@override
|
||||
String toString() => 'Failed to boot ${provider.runtimeType}: $error';
|
||||
}
|
37
packages/core/lib/src/provider/test_setup.dart
Normal file
37
packages/core/lib/src/provider/test_setup.dart
Normal file
|
@ -0,0 +1,37 @@
|
|||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_container/container.dart';
|
||||
import 'package:platform_container/mirrors.dart';
|
||||
import 'package:platform_core/src/provider/platform_with_providers.dart';
|
||||
|
||||
class TestSetup {
|
||||
late Application app;
|
||||
late PlatformWithProviders platformWithProviders;
|
||||
late Container _container;
|
||||
|
||||
Container get container => _container;
|
||||
|
||||
Future<void> initialize() async {
|
||||
app = Application();
|
||||
|
||||
// Create a container
|
||||
_container = Container();
|
||||
|
||||
// Set up MirrorsReflector
|
||||
Reflector.global = MirrorsReflector();
|
||||
|
||||
// Initialize PlatformWithProviders
|
||||
platformWithProviders = PlatformWithProviders(app);
|
||||
|
||||
// Configure the app to use our container
|
||||
app.configure((angel) {
|
||||
angel.container.registerSingleton<Container>((c) => _container);
|
||||
});
|
||||
|
||||
// Allow some time for initialization
|
||||
await Future.delayed(Duration(milliseconds: 100));
|
||||
}
|
||||
|
||||
Future<void> tearDown() async {
|
||||
await app.close();
|
||||
}
|
||||
}
|
|
@ -78,6 +78,70 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
build:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build
|
||||
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
build_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_config
|
||||
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
build_daemon:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_daemon
|
||||
sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
build_resolvers:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_resolvers
|
||||
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.2"
|
||||
build_runner:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
sha256: "644dc98a0f179b872f612d3eb627924b578897c629788e858157fa5e704ca0c7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.11"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_runner_core
|
||||
sha256: e3c79f69a64bdfcd8a776a3c28db4eb6e3fb5356d013ae5eb2e52007706d5dbe
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.3.1"
|
||||
built_collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: built_collection
|
||||
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.1.1"
|
||||
built_value:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: built_value
|
||||
sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.9.2"
|
||||
charcode:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -86,6 +150,22 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
checked_yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: checked_yaml
|
||||
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
code_builder:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: code_builder
|
||||
sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.10.0"
|
||||
collection:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -118,6 +198,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.5"
|
||||
dart_style:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dart_style
|
||||
sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.7"
|
||||
file:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -150,6 +238,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
graphs:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: graphs
|
||||
sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
http:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
|
@ -198,6 +294,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.1"
|
||||
json_annotation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: json_annotation
|
||||
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.9.0"
|
||||
lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
|
@ -246,6 +350,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.6"
|
||||
mockito:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: mockito
|
||||
sha256: "6841eed20a7befac0ce07df8116c8b8233ed1f4486a7647c7fc5a02ae6163917"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.4.4"
|
||||
node_preamble:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -321,6 +433,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
pubspec_parse:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pubspec_parse
|
||||
sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
quiver:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -369,6 +489,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
source_gen:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_gen
|
||||
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.0"
|
||||
source_map_stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -417,6 +545,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
stream_transform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_transform
|
||||
sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
string_scanner:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -457,6 +593,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.5"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: timing
|
||||
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
tuple:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
@ -36,6 +36,8 @@ dev_dependencies:
|
|||
io: ^1.0.0
|
||||
test: ^1.24.0
|
||||
lints: ^4.0.0
|
||||
mockito: ^5.4.4
|
||||
build_runner: ^2.0.0
|
||||
# dependency_overrides:
|
||||
# platform_container:
|
||||
# path: ../container/angel_container
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_container/container.dart';
|
||||
import 'package:platform_core/src/provider/platform_with_providers.dart';
|
||||
import 'package:platform_core/src/provider/service_provider.dart';
|
||||
import 'package:platform_core/src/provider/service_provider_manager.dart';
|
||||
import 'package:platform_core/src/provider/provider_discovery.dart';
|
||||
|
||||
@GenerateMocks([ServiceProviderManager, ProviderDiscovery])
|
||||
import 'platform_with_providers_test.mocks.dart';
|
||||
|
||||
void main() {
|
||||
group('PlatformWithProviders', () {
|
||||
late PlatformWithProviders platformWithProviders;
|
||||
late Application app;
|
||||
late MockServiceProviderManager mockManager;
|
||||
|
||||
setUp(() {
|
||||
app = Application();
|
||||
mockManager = MockServiceProviderManager();
|
||||
|
||||
platformWithProviders =
|
||||
PlatformWithProviders(app, serviceProviderManager: mockManager);
|
||||
});
|
||||
|
||||
test('PlatformWithProviders initializes correctly', () {
|
||||
expect(platformWithProviders, isNotNull);
|
||||
expect(platformWithProviders.app, equals(app));
|
||||
});
|
||||
|
||||
test('container is created', () {
|
||||
expect(platformWithProviders.container, isNotNull);
|
||||
expect(platformWithProviders.container, isA<Container>());
|
||||
});
|
||||
|
||||
test('registerServiceProvider calls manager.register', () async {
|
||||
final provider = MockServiceProvider();
|
||||
await platformWithProviders.registerServiceProvider(provider);
|
||||
|
||||
verify(mockManager.register(provider)).called(1);
|
||||
});
|
||||
|
||||
test('bootServiceProviders calls manager.bootAll', () async {
|
||||
await platformWithProviders.bootServiceProviders();
|
||||
|
||||
verify(mockManager.bootAll()).called(1);
|
||||
});
|
||||
|
||||
// Add more tests for other methods in PlatformWithProviders
|
||||
});
|
||||
}
|
||||
|
||||
class MockServiceProvider extends Mock implements ServiceProvider {}
|
1526
packages/core/test/provider/platform_with_providers_test.mocks.dart
Normal file
1526
packages/core/test/provider/platform_with_providers_test.mocks.dart
Normal file
File diff suppressed because it is too large
Load diff
33
packages/core/test/provider/provider_discovery_test.dart
Normal file
33
packages/core/test/provider/provider_discovery_test.dart
Normal file
|
@ -0,0 +1,33 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:platform_core/src/provider/provider_discovery.dart';
|
||||
import 'package:platform_core/src/provider/service_provider.dart';
|
||||
|
||||
class TestServiceProvider extends ServiceProvider {
|
||||
@override
|
||||
Future<void> register() async {}
|
||||
|
||||
@override
|
||||
Future<void> boot() async {}
|
||||
}
|
||||
|
||||
void main() {
|
||||
group('ManualProviderDiscovery', () {
|
||||
late ManualProviderDiscovery discovery;
|
||||
|
||||
setUp(() {
|
||||
discovery = ManualProviderDiscovery();
|
||||
});
|
||||
|
||||
test('registerProviderType adds type and factory', () {
|
||||
discovery.registerProviderType(
|
||||
TestServiceProvider, () => TestServiceProvider());
|
||||
|
||||
expect(discovery.discoverProviders(), contains(TestServiceProvider));
|
||||
expect(discovery.createInstance(TestServiceProvider),
|
||||
isA<TestServiceProvider>());
|
||||
});
|
||||
});
|
||||
|
||||
// Note: Testing MirrorProviderDiscovery is challenging in a unit test environment
|
||||
// due to its reliance on runtime reflection. Consider integration tests for this.
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:platform_core/src/provider/service_provider_config.dart';
|
||||
|
||||
void main() {
|
||||
group('ServiceProviderConfig', () {
|
||||
test('fromMap creates correct config', () {
|
||||
final map = {
|
||||
'providers': [
|
||||
{
|
||||
'type': 'TestServiceProvider',
|
||||
'deferred': true,
|
||||
'config': {'key': 'value'}
|
||||
},
|
||||
{'type': 'AnotherServiceProvider', 'deferred': false},
|
||||
]
|
||||
};
|
||||
|
||||
final config = ServiceProviderConfig.fromMap(map);
|
||||
|
||||
expect(config.providers.length, equals(2));
|
||||
expect(config.deferredProviders.length, equals(1));
|
||||
expect(config.providerConfigs.length, equals(1));
|
||||
// Note: This test assumes you've implemented _getTypeFromString to handle these provider types
|
||||
// You might need to adjust this part of the test based on your actual implementation
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/src/provider/platform_with_providers.dart';
|
||||
import 'package:platform_core/src/provider/service_provider.dart';
|
||||
import 'package:platform_core/src/provider/service_provider_config.dart';
|
||||
|
||||
class TestService {
|
||||
String getData() => 'Test Data';
|
||||
}
|
||||
|
||||
class TestServiceProvider extends ServiceProvider {
|
||||
@override
|
||||
Future<void> register() async {
|
||||
container.registerSingleton((container) => TestService());
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> boot() async {
|
||||
await Future.delayed(Duration(milliseconds: 100));
|
||||
}
|
||||
}
|
||||
|
||||
class DeferredServiceProvider extends ServiceProvider {
|
||||
@override
|
||||
Future<void> register() async {
|
||||
container.registerSingleton((container) => 'Deferred Service');
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> boot() async {
|
||||
await Future.delayed(Duration(milliseconds: 100));
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
late Application app;
|
||||
late PlatformWithProviders platformWithProviders;
|
||||
|
||||
setUp(() async {
|
||||
app = Application();
|
||||
platformWithProviders = PlatformWithProviders(app);
|
||||
// Allow some time for the platform to initialize
|
||||
await Future.delayed(Duration(milliseconds: 100));
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
group('Service Provider Integration Tests', () {
|
||||
test('Manual provider registration works', () async {
|
||||
await platformWithProviders
|
||||
.registerServiceProvider(TestServiceProvider());
|
||||
await platformWithProviders.bootServiceProviders();
|
||||
|
||||
var testService = platformWithProviders.container.make<TestService>();
|
||||
expect(testService, isNotNull);
|
||||
expect(testService.getData(), equals('Test Data'));
|
||||
});
|
||||
|
||||
test('Deferred provider is not booted initially', () async {
|
||||
var config = ServiceProviderConfig(
|
||||
providers: [DeferredServiceProvider],
|
||||
deferredProviders: {DeferredServiceProvider: true},
|
||||
);
|
||||
await platformWithProviders.registerProvidersFromConfig(config);
|
||||
await platformWithProviders.bootServiceProviders();
|
||||
|
||||
expect(() => platformWithProviders.container.make<String>(),
|
||||
throwsException);
|
||||
});
|
||||
|
||||
test('Deferred provider can be booted on demand', () async {
|
||||
var config = ServiceProviderConfig(
|
||||
providers: [DeferredServiceProvider],
|
||||
deferredProviders: {DeferredServiceProvider: true},
|
||||
);
|
||||
await platformWithProviders.registerProvidersFromConfig(config);
|
||||
await platformWithProviders.bootServiceProviders();
|
||||
await platformWithProviders
|
||||
.bootDeferredServiceProvider(DeferredServiceProvider);
|
||||
|
||||
var deferredService = platformWithProviders.container.make<String>();
|
||||
expect(deferredService, equals('Deferred Service'));
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/src/provider/service_provider_manager.dart';
|
||||
import 'package:platform_core/src/provider/service_provider.dart';
|
||||
import 'package:platform_core/src/provider/provider_discovery.dart';
|
||||
import 'package:platform_container/container.dart' as container;
|
||||
import 'service_provider_manager_test.mocks.dart';
|
||||
|
||||
//class MockAngel extends Mock implements Application {}
|
||||
|
||||
//class MockServiceProvider extends Mock implements ServiceProvider {}
|
||||
|
||||
//class MockProviderDiscovery extends Mock implements ProviderDiscovery {}
|
||||
|
||||
// Generate mocks
|
||||
@GenerateMocks(
|
||||
[Application, ServiceProvider, ProviderDiscovery, container.Container])
|
||||
void main() {
|
||||
group('ServiceProviderManager', () {
|
||||
late ServiceProviderManager manager;
|
||||
late Application mockAngel;
|
||||
late MockProviderDiscovery mockDiscovery;
|
||||
late MockContainer mockContainer;
|
||||
|
||||
setUp(() {
|
||||
mockAngel = Application();
|
||||
mockDiscovery = MockProviderDiscovery();
|
||||
mockContainer = MockContainer();
|
||||
when(mockAngel.container).thenReturn(mockContainer);
|
||||
manager = ServiceProviderManager(mockAngel, mockDiscovery);
|
||||
});
|
||||
|
||||
test('register adds provider and calls register method', () async {
|
||||
final provider = MockServiceProvider();
|
||||
await manager.register(provider);
|
||||
|
||||
verify(provider.registerWithContainer(any)).called(1);
|
||||
verify(provider.register()).called(1);
|
||||
});
|
||||
|
||||
test('bootAll calls boot for all providers', () async {
|
||||
final provider1 = MockServiceProvider();
|
||||
final provider2 = MockServiceProvider();
|
||||
|
||||
when(provider1.isEnabled).thenReturn(true);
|
||||
when(provider2.isEnabled).thenReturn(true);
|
||||
when(provider1.isDeferred).thenReturn(false);
|
||||
when(provider2.isDeferred).thenReturn(false);
|
||||
|
||||
await manager.register(provider1);
|
||||
await manager.register(provider2);
|
||||
|
||||
await manager.bootAll();
|
||||
|
||||
verify(provider1.beforeBoot()).called(1);
|
||||
verify(provider1.boot()).called(1);
|
||||
verify(provider1.afterBoot()).called(1);
|
||||
verify(provider2.beforeBoot()).called(1);
|
||||
verify(provider2.boot()).called(1);
|
||||
verify(provider2.afterBoot()).called(1);
|
||||
});
|
||||
|
||||
test('bootDeferredProvider boots only the specified provider', () async {
|
||||
final provider1 = MockServiceProvider();
|
||||
final provider2 = MockServiceProvider();
|
||||
|
||||
when(provider1.isDeferred).thenReturn(true);
|
||||
when(provider2.isDeferred).thenReturn(true);
|
||||
|
||||
await manager.register(provider1);
|
||||
await manager.register(provider2);
|
||||
|
||||
await manager.bootDeferredProvider(provider1.runtimeType);
|
||||
|
||||
verify(provider1.beforeBoot()).called(1);
|
||||
verify(provider1.boot()).called(1);
|
||||
verify(provider1.afterBoot()).called(1);
|
||||
verifyNever(provider2.beforeBoot());
|
||||
verifyNever(provider2.boot());
|
||||
verifyNever(provider2.afterBoot());
|
||||
});
|
||||
|
||||
test('unregister removes the provider', () async {
|
||||
final provider = MockServiceProvider();
|
||||
await manager.register(provider);
|
||||
manager.unregister(provider.runtimeType);
|
||||
|
||||
expect(manager.getProvider<MockServiceProvider>(), isNull);
|
||||
});
|
||||
});
|
||||
}
|
1462
packages/core/test/provider/service_provider_manager_test.mocks.dart
Normal file
1462
packages/core/test/provider/service_provider_manager_test.mocks.dart
Normal file
File diff suppressed because it is too large
Load diff
32
packages/core/test/provider/service_provider_test.dart
Normal file
32
packages/core/test/provider/service_provider_test.dart
Normal file
|
@ -0,0 +1,32 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:platform_core/src/provider/service_provider.dart';
|
||||
|
||||
class MockServiceProvider extends Mock implements ServiceProvider {}
|
||||
|
||||
void main() {
|
||||
group('ServiceProvider', () {
|
||||
late ServiceProvider provider;
|
||||
|
||||
setUp(() {
|
||||
provider = MockServiceProvider();
|
||||
});
|
||||
|
||||
test('setEnabled changes isEnabled', () {
|
||||
when(provider.isEnabled).thenAnswer((_) => true);
|
||||
provider.setEnabled(false);
|
||||
verify(provider.setEnabled(false)).called(1);
|
||||
});
|
||||
|
||||
test('setDeferred changes isDeferred', () {
|
||||
when(provider.isDeferred).thenAnswer((_) => false);
|
||||
provider.setDeferred(true);
|
||||
verify(provider.setDeferred(true)).called(1);
|
||||
});
|
||||
|
||||
test('configure adds to config', () {
|
||||
provider.configure({'key': 'value'});
|
||||
verify(provider.configure({'key': 'value'})).called(1);
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue