mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-28 01:32:57 +01:00
Support asynchronous data factories #850
This commit is contained in:
parent
c0c71f82c0
commit
af9fb5d190
@ -2,10 +2,15 @@
|
|||||||
|
|
||||||
## [1.0.3] - unreleased
|
## [1.0.3] - unreleased
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Support asynchronous data factories #850
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
* Use `encoding_rs` crate instead of unmaintained `encoding` crate
|
* Use `encoding_rs` crate instead of unmaintained `encoding` crate
|
||||||
|
|
||||||
|
|
||||||
## [1.0.2] - 2019-06-17
|
## [1.0.2] - 2019-06-17
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
@ -17,7 +22,7 @@
|
|||||||
|
|
||||||
## [1.0.1] - 2019-06-17
|
## [1.0.1] - 2019-06-17
|
||||||
|
|
||||||
### Add
|
### Added
|
||||||
|
|
||||||
* Add support for PathConfig #903
|
* Add support for PathConfig #903
|
||||||
|
|
||||||
@ -42,7 +47,7 @@
|
|||||||
|
|
||||||
## [1.0.0] - 2019-06-05
|
## [1.0.0] - 2019-06-05
|
||||||
|
|
||||||
### Add
|
### Added
|
||||||
|
|
||||||
* Add `Scope::configure()` method.
|
* Add `Scope::configure()` method.
|
||||||
|
|
||||||
|
66
src/app.rs
66
src/app.rs
@ -8,7 +8,7 @@ use actix_service::boxed::{self, BoxedNewService};
|
|||||||
use actix_service::{
|
use actix_service::{
|
||||||
apply_transform, IntoNewService, IntoTransform, NewService, Transform,
|
apply_transform, IntoNewService, IntoTransform, NewService, Transform,
|
||||||
};
|
};
|
||||||
use futures::IntoFuture;
|
use futures::{Future, IntoFuture};
|
||||||
|
|
||||||
use crate::app_service::{AppEntry, AppInit, AppRoutingFactory};
|
use crate::app_service::{AppEntry, AppInit, AppRoutingFactory};
|
||||||
use crate::config::{AppConfig, AppConfigInner, ServiceConfig};
|
use crate::config::{AppConfig, AppConfigInner, ServiceConfig};
|
||||||
@ -23,6 +23,7 @@ use crate::service::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
type HttpNewService = BoxedNewService<(), ServiceRequest, ServiceResponse, Error, ()>;
|
type HttpNewService = BoxedNewService<(), ServiceRequest, ServiceResponse, Error, ()>;
|
||||||
|
type FnDataFactory = Box<Fn() -> Box<dyn Future<Item = Box<DataFactory>, Error = ()>>>;
|
||||||
|
|
||||||
/// Application builder - structure that follows the builder pattern
|
/// Application builder - structure that follows the builder pattern
|
||||||
/// for building application instances.
|
/// for building application instances.
|
||||||
@ -32,6 +33,7 @@ pub struct App<T, B> {
|
|||||||
default: Option<Rc<HttpNewService>>,
|
default: Option<Rc<HttpNewService>>,
|
||||||
factory_ref: Rc<RefCell<Option<AppRoutingFactory>>>,
|
factory_ref: Rc<RefCell<Option<AppRoutingFactory>>>,
|
||||||
data: Vec<Box<DataFactory>>,
|
data: Vec<Box<DataFactory>>,
|
||||||
|
data_factories: Vec<FnDataFactory>,
|
||||||
config: AppConfigInner,
|
config: AppConfigInner,
|
||||||
external: Vec<ResourceDef>,
|
external: Vec<ResourceDef>,
|
||||||
_t: PhantomData<(B)>,
|
_t: PhantomData<(B)>,
|
||||||
@ -44,6 +46,7 @@ impl App<AppEntry, Body> {
|
|||||||
App {
|
App {
|
||||||
endpoint: AppEntry::new(fref.clone()),
|
endpoint: AppEntry::new(fref.clone()),
|
||||||
data: Vec::new(),
|
data: Vec::new(),
|
||||||
|
data_factories: Vec::new(),
|
||||||
services: Vec::new(),
|
services: Vec::new(),
|
||||||
default: None,
|
default: None,
|
||||||
factory_ref: fref,
|
factory_ref: fref,
|
||||||
@ -100,6 +103,31 @@ where
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set application data factory. This function is
|
||||||
|
/// similar to `.data()` but it accepts data factory. Data object get
|
||||||
|
/// constructed asynchronously during application initialization.
|
||||||
|
pub fn data_factory<F, Out>(mut self, data: F) -> Self
|
||||||
|
where
|
||||||
|
F: Fn() -> Out + 'static,
|
||||||
|
Out: IntoFuture + 'static,
|
||||||
|
Out::Error: std::fmt::Debug,
|
||||||
|
{
|
||||||
|
self.data_factories.push(Box::new(move || {
|
||||||
|
Box::new(
|
||||||
|
data()
|
||||||
|
.into_future()
|
||||||
|
.map_err(|e| {
|
||||||
|
log::error!("Can not construct data instance: {:?}", e);
|
||||||
|
})
|
||||||
|
.map(|data| {
|
||||||
|
let data: Box<dyn DataFactory> = Box::new(Data::new(data));
|
||||||
|
data
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Set application data. Application data could be accessed
|
/// Set application data. Application data could be accessed
|
||||||
/// by using `Data<T>` extractor where `T` is data type.
|
/// by using `Data<T>` extractor where `T` is data type.
|
||||||
pub fn register_data<U: 'static>(mut self, data: Data<U>) -> Self {
|
pub fn register_data<U: 'static>(mut self, data: Data<U>) -> Self {
|
||||||
@ -349,6 +377,7 @@ where
|
|||||||
App {
|
App {
|
||||||
endpoint,
|
endpoint,
|
||||||
data: self.data,
|
data: self.data,
|
||||||
|
data_factories: self.data_factories,
|
||||||
services: self.services,
|
services: self.services,
|
||||||
default: self.default,
|
default: self.default,
|
||||||
factory_ref: self.factory_ref,
|
factory_ref: self.factory_ref,
|
||||||
@ -423,6 +452,7 @@ where
|
|||||||
fn into_new_service(self) -> AppInit<T, B> {
|
fn into_new_service(self) -> AppInit<T, B> {
|
||||||
AppInit {
|
AppInit {
|
||||||
data: Rc::new(self.data),
|
data: Rc::new(self.data),
|
||||||
|
data_factories: Rc::new(self.data_factories),
|
||||||
endpoint: self.endpoint,
|
endpoint: self.endpoint,
|
||||||
services: Rc::new(RefCell::new(self.services)),
|
services: Rc::new(RefCell::new(self.services)),
|
||||||
external: RefCell::new(self.external),
|
external: RefCell::new(self.external),
|
||||||
@ -490,24 +520,24 @@ mod tests {
|
|||||||
assert_eq!(resp.status(), StatusCode::CREATED);
|
assert_eq!(resp.status(), StatusCode::CREATED);
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[test]
|
#[test]
|
||||||
// fn test_data_factory() {
|
fn test_data_factory() {
|
||||||
// let mut srv =
|
let mut srv =
|
||||||
// init_service(App::new().data_factory(|| Ok::<_, ()>(10usize)).service(
|
init_service(App::new().data_factory(|| Ok::<_, ()>(10usize)).service(
|
||||||
// web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
|
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
|
||||||
// ));
|
));
|
||||||
// let req = TestRequest::default().to_request();
|
let req = TestRequest::default().to_request();
|
||||||
// let resp = block_on(srv.call(req)).unwrap();
|
let resp = block_on(srv.call(req)).unwrap();
|
||||||
// assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
|
|
||||||
// let mut srv =
|
let mut srv =
|
||||||
// init_service(App::new().data_factory(|| Ok::<_, ()>(10u32)).service(
|
init_service(App::new().data_factory(|| Ok::<_, ()>(10u32)).service(
|
||||||
// web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
|
web::resource("/").to(|_: web::Data<usize>| HttpResponse::Ok()),
|
||||||
// ));
|
));
|
||||||
// let req = TestRequest::default().to_request();
|
let req = TestRequest::default().to_request();
|
||||||
// let resp = block_on(srv.call(req)).unwrap();
|
let resp = block_on(srv.call(req)).unwrap();
|
||||||
// assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||||
// }
|
}
|
||||||
|
|
||||||
fn md<S, B>(
|
fn md<S, B>(
|
||||||
req: ServiceRequest,
|
req: ServiceRequest,
|
||||||
|
@ -25,6 +25,7 @@ type BoxedResponse = Either<
|
|||||||
FutureResult<ServiceResponse, Error>,
|
FutureResult<ServiceResponse, Error>,
|
||||||
Box<Future<Item = ServiceResponse, Error = Error>>,
|
Box<Future<Item = ServiceResponse, Error = Error>>,
|
||||||
>;
|
>;
|
||||||
|
type FnDataFactory = Box<Fn() -> Box<dyn Future<Item = Box<DataFactory>, Error = ()>>>;
|
||||||
|
|
||||||
/// Service factory to convert `Request` to a `ServiceRequest<S>`.
|
/// Service factory to convert `Request` to a `ServiceRequest<S>`.
|
||||||
/// It also executes data factories.
|
/// It also executes data factories.
|
||||||
@ -40,6 +41,7 @@ where
|
|||||||
{
|
{
|
||||||
pub(crate) endpoint: T,
|
pub(crate) endpoint: T,
|
||||||
pub(crate) data: Rc<Vec<Box<DataFactory>>>,
|
pub(crate) data: Rc<Vec<Box<DataFactory>>>,
|
||||||
|
pub(crate) data_factories: Rc<Vec<FnDataFactory>>,
|
||||||
pub(crate) config: RefCell<AppConfig>,
|
pub(crate) config: RefCell<AppConfig>,
|
||||||
pub(crate) services: Rc<RefCell<Vec<Box<ServiceFactory>>>>,
|
pub(crate) services: Rc<RefCell<Vec<Box<ServiceFactory>>>>,
|
||||||
pub(crate) default: Option<Rc<HttpNewService>>,
|
pub(crate) default: Option<Rc<HttpNewService>>,
|
||||||
@ -119,16 +121,12 @@ where
|
|||||||
let rmap = Rc::new(rmap);
|
let rmap = Rc::new(rmap);
|
||||||
rmap.finish(rmap.clone());
|
rmap.finish(rmap.clone());
|
||||||
|
|
||||||
// create app data container
|
|
||||||
let mut data = Extensions::new();
|
|
||||||
for f in self.data.iter() {
|
|
||||||
f.create(&mut data);
|
|
||||||
}
|
|
||||||
|
|
||||||
AppInitResult {
|
AppInitResult {
|
||||||
endpoint: None,
|
endpoint: None,
|
||||||
endpoint_fut: self.endpoint.new_service(&()),
|
endpoint_fut: self.endpoint.new_service(&()),
|
||||||
data: Rc::new(data),
|
data: self.data.clone(),
|
||||||
|
data_factories: Vec::new(),
|
||||||
|
data_factories_fut: self.data_factories.iter().map(|f| f()).collect(),
|
||||||
config,
|
config,
|
||||||
rmap,
|
rmap,
|
||||||
_t: PhantomData,
|
_t: PhantomData,
|
||||||
@ -144,7 +142,9 @@ where
|
|||||||
endpoint_fut: T::Future,
|
endpoint_fut: T::Future,
|
||||||
rmap: Rc<ResourceMap>,
|
rmap: Rc<ResourceMap>,
|
||||||
config: AppConfig,
|
config: AppConfig,
|
||||||
data: Rc<Extensions>,
|
data: Rc<Vec<Box<DataFactory>>>,
|
||||||
|
data_factories: Vec<Box<DataFactory>>,
|
||||||
|
data_factories_fut: Vec<Box<dyn Future<Item = Box<DataFactory>, Error = ()>>>,
|
||||||
_t: PhantomData<B>,
|
_t: PhantomData<B>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,21 +159,43 @@ where
|
|||||||
>,
|
>,
|
||||||
{
|
{
|
||||||
type Item = AppInitService<T::Service, B>;
|
type Item = AppInitService<T::Service, B>;
|
||||||
type Error = T::InitError;
|
type Error = ();
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||||
|
// async data factories
|
||||||
|
let mut idx = 0;
|
||||||
|
while idx < self.data_factories_fut.len() {
|
||||||
|
match self.data_factories_fut[idx].poll()? {
|
||||||
|
Async::Ready(f) => {
|
||||||
|
self.data_factories.push(f);
|
||||||
|
self.data_factories_fut.remove(idx);
|
||||||
|
}
|
||||||
|
Async::NotReady => idx += 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if self.endpoint.is_none() {
|
if self.endpoint.is_none() {
|
||||||
if let Async::Ready(srv) = self.endpoint_fut.poll()? {
|
if let Async::Ready(srv) = self.endpoint_fut.poll()? {
|
||||||
self.endpoint = Some(srv);
|
self.endpoint = Some(srv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.endpoint.is_some() {
|
if self.endpoint.is_some() && self.data_factories_fut.is_empty() {
|
||||||
|
// create app data container
|
||||||
|
let mut data = Extensions::new();
|
||||||
|
for f in self.data.iter() {
|
||||||
|
f.create(&mut data);
|
||||||
|
}
|
||||||
|
|
||||||
|
for f in &self.data_factories {
|
||||||
|
f.create(&mut data);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Async::Ready(AppInitService {
|
Ok(Async::Ready(AppInitService {
|
||||||
service: self.endpoint.take().unwrap(),
|
service: self.endpoint.take().unwrap(),
|
||||||
rmap: self.rmap.clone(),
|
rmap: self.rmap.clone(),
|
||||||
config: self.config.clone(),
|
config: self.config.clone(),
|
||||||
data: self.data.clone(),
|
data: Rc::new(data),
|
||||||
pool: HttpRequestPool::create(),
|
pool: HttpRequestPool::create(),
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user