Skip to content

Commit

Permalink
add external resource support on scope level
Browse files Browse the repository at this point in the history
  • Loading branch information
fafhrd91 committed Jun 3, 2019
1 parent 1fce487 commit 0e138e1
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 75 deletions.
4 changes: 3 additions & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# Changes

## [1.0.0] - 2019-05-xx
## [1.0.0] - 2019-06-xx

### Add

* Add `Scope::configure()` method.

* Add `ServiceRequest::set_payload()` method.

* Add `test::TestRequest::set_json()` convenience method to automatically
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ rustls = { version = "0.15", optional = true }
[dev-dependencies]
actix-http = { version = "0.2.2", features=["ssl", "brotli", "flate2-zlib"] }
actix-http-test = { version = "0.2.0", features=["ssl"] }
actix-files = { version = "0.1.0" }
actix-files = { version = "0.1.1" }
rand = "0.6"
env_logger = "0.6"
serde_derive = "1.0"
Expand Down
4 changes: 3 additions & 1 deletion src/rmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,9 @@ impl ResourceMap {
I: AsRef<str>,
{
if let Some(pattern) = self.named.get(name) {
self.fill_root(path, elements)?;
if pattern.pattern().starts_with("/") {
self.fill_root(path, elements)?;
}
if pattern.resource_path(path, elements) {
Ok(Some(()))
} else {
Expand Down
170 changes: 98 additions & 72 deletions src/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use actix_service::{
use futures::future::{ok, Either, Future, FutureResult};
use futures::{Async, IntoFuture, Poll};

use crate::config::ServiceConfig;
use crate::data::Data;
use crate::dev::{AppService, HttpServiceFactory};
use crate::error::Error;
Expand All @@ -21,7 +22,6 @@ use crate::route::Route;
use crate::service::{
ServiceFactory, ServiceFactoryWrapper, ServiceRequest, ServiceResponse,
};
use crate::config::ServiceConfig;

type Guards = Vec<Box<Guard>>;
type HttpService = BoxedService<ServiceRequest, ServiceResponse, Error>;
Expand Down Expand Up @@ -67,6 +67,7 @@ pub struct Scope<T = ScopeEndpoint> {
services: Vec<Box<ServiceFactory>>,
guards: Vec<Box<Guard>>,
default: Rc<RefCell<Option<Rc<HttpNewService>>>>,
external: Vec<ResourceDef>,
factory_ref: Rc<RefCell<Option<ScopeFactory>>>,
}

Expand All @@ -81,59 +82,10 @@ impl Scope {
guards: Vec::new(),
services: Vec::new(),
default: Rc::new(RefCell::new(None)),
external: Vec::new(),
factory_ref: fref,
}
}


/// Run external configuration as part of the scope building
/// process
///
/// This function is useful for moving parts of configuration to a
/// different module or even library. For example,
/// some of the resource's configuration could be moved to different module.
///
/// ```rust
/// # extern crate actix_web;
/// use actix_web::{web, middleware, App, HttpResponse};
///
/// // this function could be located in different module
/// fn config(cfg: &mut web::ServiceConfig) {
/// cfg.service(web::resource("/test")
/// .route(web::get().to(|| HttpResponse::Ok()))
/// .route(web::head().to(|| HttpResponse::MethodNotAllowed()))
/// );
/// }
///
/// fn main() {
/// let app = App::new()
/// .wrap(middleware::Logger::default())
/// .service(
/// web::scope("/api")
/// .configure(config)
/// )
/// .route("/index.html", web::get().to(|| HttpResponse::Ok()));
/// }
/// ```
pub fn configure<F>(mut self, f: F) -> Self
where
F: FnOnce(&mut ServiceConfig),
{
let mut cfg = ServiceConfig::new();
f(&mut cfg);
self.services.extend(cfg.services);

if !cfg.data.is_empty() {
let mut data = self.data.unwrap_or(Extensions::new());

for value in cfg.data.iter() {
value.create(&mut data);
}

self.data = Some(data);
}
self
}
}

impl<T> Scope<T>
Expand Down Expand Up @@ -204,6 +156,56 @@ where
self
}

/// Run external configuration as part of the scope building
/// process
///
/// This function is useful for moving parts of configuration to a
/// different module or even library. For example,
/// some of the resource's configuration could be moved to different module.
///
/// ```rust
/// # extern crate actix_web;
/// use actix_web::{web, middleware, App, HttpResponse};
///
/// // this function could be located in different module
/// fn config(cfg: &mut web::ServiceConfig) {
/// cfg.service(web::resource("/test")
/// .route(web::get().to(|| HttpResponse::Ok()))
/// .route(web::head().to(|| HttpResponse::MethodNotAllowed()))
/// );
/// }
///
/// fn main() {
/// let app = App::new()
/// .wrap(middleware::Logger::default())
/// .service(
/// web::scope("/api")
/// .configure(config)
/// )
/// .route("/index.html", web::get().to(|| HttpResponse::Ok()));
/// }
/// ```
pub fn configure<F>(mut self, f: F) -> Self
where
F: FnOnce(&mut ServiceConfig),
{
let mut cfg = ServiceConfig::new();
f(&mut cfg);
self.services.extend(cfg.services);
self.external.extend(cfg.external);

if !cfg.data.is_empty() {
let mut data = self.data.unwrap_or_else(|| Extensions::new());

for value in cfg.data.iter() {
value.create(&mut data);
}

self.data = Some(data);
}
self
}

/// Register http service.
///
/// This is similar to `App's` service registration.
Expand Down Expand Up @@ -332,6 +334,7 @@ where
guards: self.guards,
services: self.services,
default: self.default,
external: self.external,
factory_ref: self.factory_ref,
}
}
Expand Down Expand Up @@ -410,6 +413,11 @@ where

let mut rmap = ResourceMap::new(ResourceDef::root_prefix(&self.rdef));

// external resources
for mut rdef in std::mem::replace(&mut self.external, Vec::new()) {
rmap.add(&mut rdef, None);
}

// custom app data storage
if let Some(ref mut ext) = self.data {
config.set_service_data(ext);
Expand Down Expand Up @@ -645,7 +653,7 @@ mod tests {
use crate::dev::{Body, ResponseBody};
use crate::http::{header, HeaderValue, Method, StatusCode};
use crate::service::{ServiceRequest, ServiceResponse};
use crate::test::{block_on, call_service, init_service, TestRequest};
use crate::test::{block_on, call_service, init_service, read_body, TestRequest};
use crate::{guard, web, App, Error, HttpRequest, HttpResponse};

#[test]
Expand Down Expand Up @@ -1076,14 +1084,10 @@ mod tests {

#[test]
fn test_scope_config() {
let mut srv = init_service(
App::new().service(
web::scope("/app")
.configure(|s|{
s.route("/path1", web::get().to(||HttpResponse::Ok()));
})
),
);
let mut srv =
init_service(App::new().service(web::scope("/app").configure(|s| {
s.route("/path1", web::get().to(|| HttpResponse::Ok()));
})));

let req = TestRequest::with_uri("/app/path1").to_request();
let resp = block_on(srv.call(req)).unwrap();
Expand All @@ -1092,21 +1096,43 @@ mod tests {

#[test]
fn test_scope_config_2() {
let mut srv = init_service(
App::new().service(
web::scope("/app")
.configure(|s|{
s.service(
web::scope("/v1")
.configure(|s|{
s.route("/", web::get().to(||HttpResponse::Ok()));
}));
})
),
);
let mut srv =
init_service(App::new().service(web::scope("/app").configure(|s| {
s.service(web::scope("/v1").configure(|s| {
s.route("/", web::get().to(|| HttpResponse::Ok()));
}));
})));

let req = TestRequest::with_uri("/app/v1/").to_request();
let resp = block_on(srv.call(req)).unwrap();
assert_eq!(resp.status(), StatusCode::OK);
}

#[test]
fn test_url_for_external() {
let mut srv =
init_service(App::new().service(web::scope("/app").configure(|s| {
s.service(web::scope("/v1").configure(|s| {
s.external_resource(
"youtube",
"https://youtube.com/watch/{video_id}",
);
s.route(
"/",
web::get().to(|req: HttpRequest| {
HttpResponse::Ok().body(format!(
"{}",
req.url_for("youtube", &["xxxxxx"]).unwrap().as_str()
))
}),
);
}));
})));

let req = TestRequest::with_uri("/app/v1/").to_request();
let resp = block_on(srv.call(req)).unwrap();
assert_eq!(resp.status(), StatusCode::OK);
let body = read_body(resp);
assert_eq!(body, &b"https://youtube.com/watch/xxxxxx"[..]);
}
}

0 comments on commit 0e138e1

Please sign in to comment.