1
0
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:
Nikolay Kim 2019-06-28 10:43:52 +06:00
parent c0c71f82c0
commit af9fb5d190
3 changed files with 88 additions and 31 deletions

View File

@ -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.

View File

@ -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,

View File

@ -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 {