diff --git a/Cargo.lock b/Cargo.lock index 41aa63574..058e2de4b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,6 +46,7 @@ version = "0.1.0" dependencies = [ "async-std", "chrono", + "log", "num-integer", "once_cell", "primitives", @@ -53,7 +54,6 @@ dependencies = [ "reqwest", "serde", "serde_json", - "slog", "thiserror", "url", ] @@ -409,6 +409,25 @@ dependencies = [ "once_cell", ] +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "memchr", +] + +[[package]] +name = "buf_redux" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" +dependencies = [ + "memchr", + "safemem", +] + [[package]] name = "bumpalo" version = "3.6.1" @@ -483,6 +502,28 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "chrono-tz" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58549f1842da3080ce63002102d5bc954c7bc843d4f47818e642abdc36253552" +dependencies = [ + "chrono", + "chrono-tz-build", + "phf 0.10.1", +] + +[[package]] +name = "chrono-tz-build" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db058d493fb2f65f41861bfed7e3fe6335264a9f0f92710cab5bdf01fef09069" +dependencies = [ + "parse-zoneinfo", + "phf 0.10.1", + "phf_codegen", +] + [[package]] name = "cid" version = "0.8.3" @@ -846,6 +887,12 @@ dependencies = [ "syn", ] +[[package]] +name = "deunicode" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "850878694b7933ca4c9569d30a34b55031b9b139ee1fc7b94a527c4ef960d690" + [[package]] name = "diff" version = "0.1.12" @@ -928,6 +975,19 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "env_logger" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + [[package]] name = "envy" version = "0.4.2" @@ -1117,6 +1177,12 @@ version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + [[package]] name = "fallible-iterator" version = "0.2.0" @@ -1361,6 +1427,30 @@ version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" +[[package]] +name = "globset" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags", + "ignore", + "walkdir", +] + [[package]] name = "gloo-timers" version = "0.2.1" @@ -1411,7 +1501,7 @@ dependencies = [ "headers-core", "http", "mime", - "sha-1", + "sha-1 0.9.4", "time", ] @@ -1522,6 +1612,18 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" +[[package]] +name = "humansize" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02296996cb8796d7c6e3bc2d9211b7802812d36999a51bb754123ead7d37d026" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "hyper" version = "0.14.4" @@ -1576,6 +1678,24 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "ignore" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d" +dependencies = [ + "crossbeam-utils", + "globset", + "lazy_static", + "log", + "memchr", + "regex", + "same-file", + "thread_local", + "walkdir", + "winapi-util", +] + [[package]] name = "impl-codec" version = "0.5.1" @@ -1763,6 +1883,12 @@ dependencies = [ "value-bag", ] +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + [[package]] name = "matches" version = "0.1.8" @@ -1858,6 +1984,16 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "miniz_oxide" version = "0.4.4" @@ -1933,6 +2069,24 @@ dependencies = [ "synstructure", ] +[[package]] +name = "multipart" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182" +dependencies = [ + "buf_redux", + "httparse", + "log", + "mime", + "mime_guess", + "quick-error", + "rand 0.8.3", + "safemem", + "tempfile", + "twoway", +] + [[package]] name = "native-tls" version = "0.2.7" @@ -2322,19 +2476,100 @@ dependencies = [ "syn", ] +[[package]] +name = "parse-zoneinfo" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" +dependencies = [ + "regex", +] + [[package]] name = "percent-encoding" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" +dependencies = [ + "maplit", + "pest", + "sha-1 0.8.2", +] + [[package]] name = "phf" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" dependencies = [ - "phf_shared", + "phf_shared 0.8.0", +] + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_codegen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +dependencies = [ + "phf_generator", + "phf_shared 0.10.0", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared 0.10.0", + "rand 0.8.3", ] [[package]] @@ -2346,6 +2581,16 @@ dependencies = [ "siphasher", ] +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", + "uncased", +] + [[package]] name = "pin-project" version = "1.0.5" @@ -3068,6 +3313,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + [[package]] name = "same-file" version = "1.0.6" @@ -3087,6 +3338,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + [[package]] name = "scopeguard" version = "0.3.3" @@ -3285,6 +3542,34 @@ dependencies = [ "syn", ] +[[package]] +name = "serve" +version = "0.1.0" +dependencies = [ + "adview-manager", + "chrono", + "env_logger", + "log", + "primitives", + "serde", + "tera", + "tokio", + "warp", + "wiremock", +] + +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + [[package]] name = "sha-1" version = "0.9.4" @@ -3415,6 +3700,15 @@ dependencies = [ "thread_local", ] +[[package]] +name = "slug" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3bc762e6a4b6c6fcaade73e77f9ebc6991b676f88bb2358bddb56560f073373" +dependencies = [ + "deunicode", +] + [[package]] name = "smallvec" version = "0.6.14" @@ -3463,7 +3757,7 @@ dependencies = [ "httparse", "log", "rand 0.8.3", - "sha-1", + "sha-1 0.9.4", ] [[package]] @@ -3598,6 +3892,28 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "tera" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3cac831b615c25bcef632d1cabf864fa05813baad3d526829db18eb70e8b58d" +dependencies = [ + "chrono", + "chrono-tz", + "globwalk", + "humansize", + "lazy_static", + "percent-encoding", + "pest", + "pest_derive", + "rand 0.8.3", + "regex", + "serde", + "serde_json", + "slug", + "unic-segment", +] + [[package]] name = "term" version = "0.7.0" @@ -3773,7 +4089,7 @@ dependencies = [ "log", "parking_lot 0.11.1", "percent-encoding", - "phf", + "phf 0.8.0", "pin-project-lite", "postgres-protocol 0.6.1", "postgres-types", @@ -3793,6 +4109,19 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-tungstenite" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "511de3f85caf1c98983545490c3d09685fa8eb634e57eec22bb4db271f46cbd8" +dependencies = [ + "futures-util", + "log", + "pin-project", + "tokio", + "tungstenite", +] + [[package]] name = "tokio-util" version = "0.6.4" @@ -3830,6 +4159,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f" dependencies = [ "cfg-if 1.0.0", + "log", "pin-project-lite", "tracing-core", ] @@ -3849,12 +4179,46 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +[[package]] +name = "tungstenite" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0b2d8558abd2e276b0a8df5c05a2ec762609344191e5fd23e292c910e9165b5" +dependencies = [ + "base64", + "byteorder 1.4.3", + "bytes", + "http", + "httparse", + "log", + "rand 0.8.3", + "sha-1 0.9.4", + "thiserror", + "url", + "utf-8", +] + +[[package]] +name = "twoway" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" +dependencies = [ + "memchr", +] + [[package]] name = "typenum" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + [[package]] name = "uint" version = "0.4.1" @@ -3879,6 +4243,74 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "uncased" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baeed7327e25054889b9bd4f975f32e5f4c5d434042d59ab6cd4142c0a76ed0" +dependencies = [ + "version_check", +] + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-segment" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23" +dependencies = [ + "unic-ucd-segment", +] + +[[package]] +name = "unic-ucd-segment" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.4" @@ -3928,6 +4360,12 @@ dependencies = [ "serde", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "uuid" version = "0.8.2" @@ -4015,6 +4453,36 @@ dependencies = [ "try-lock", ] +[[package]] +name = "warp" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cef4e1e9114a4b7f1ac799f16ce71c14de5778500c5450ec6b7b920c55b587e" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "headers", + "http", + "hyper", + "log", + "mime", + "mime_guess", + "multipart", + "percent-encoding", + "pin-project", + "scoped-tls", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-stream", + "tokio-tungstenite", + "tokio-util", + "tower-service", + "tracing", +] + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 0e566c655..b0965596c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,8 @@ members = [ "primitives", "adapter", "adview-manager", + # Server for testing adview manager generated code + "adview-manager/serve", "validator_worker", "sentry", "test_harness", diff --git a/adapter/src/ethereum.rs b/adapter/src/ethereum.rs index 471bdaccf..ffebbd8e3 100644 --- a/adapter/src/ethereum.rs +++ b/adapter/src/ethereum.rs @@ -32,6 +32,7 @@ mod error; pub mod ewt; #[cfg(any(test, feature = "test-util"))] +#[cfg_attr(docsrs, doc(cfg(feature = "test-util")))] pub mod test_util; pub static OUTPACE_ABI: Lazy<&'static [u8]> = diff --git a/adview-manager/Cargo.toml b/adview-manager/Cargo.toml index 5148734e8..ab496c47b 100644 --- a/adview-manager/Cargo.toml +++ b/adview-manager/Cargo.toml @@ -16,7 +16,7 @@ serde_json = "^1.0" reqwest = { version = "0.11", features = ["json"] } url = { version = "^2.1", features = ["serde"] } # Logging -slog = { version = "^2.5.2", features = ["max_level_trace"] } +log = "0.4" # Async async-std = "^1.8" # Other diff --git a/adview-manager/README.md b/adview-manager/README.md new file mode 100644 index 000000000..1a5dde1f2 --- /dev/null +++ b/adview-manager/README.md @@ -0,0 +1,23 @@ +# AdView Manager + +Gives you the ability to generate the code for showing ads on a publisher website. + +### Testing + +There is a local server that you can run for testing purposes +This server gives you access to routes for showing ads and in terms to view, +the generated by this crate HTML/JavaScript code which is used on +a publisher websites. + +Running the local server: + +`RUST_LOG=debug cargo run -p serve` + +This will start a server at `127.0.0.1:3030` + +You can use `RUST_LOG` environment variable to set the logging level +for the server. + +Routes: + +- `GET /ad` - visualizes a single ad \ No newline at end of file diff --git a/adview-manager/serve/Cargo.toml b/adview-manager/serve/Cargo.toml new file mode 100644 index 000000000..cfd70265d --- /dev/null +++ b/adview-manager/serve/Cargo.toml @@ -0,0 +1,34 @@ +[package] +authors = ["Ambire ", "Lachezar Lechev "] +edition = "2021" +name = "serve" +version = "0.1.0" +license = "AGPL-3.0" +publish = false + +[features] + +[dependencies] +# Domain +adex_primitives = { path = "../../primitives", package = "primitives", features = ["test-util"] } +adview-manager = { path = "../" } +chrono = "0.4" + +# Async runtime +tokio = { version = "1", features = ["macros", "time", "rt-multi-thread"] } + +# Web Server +warp = { version = "0.3" } + +# Template engine +tera = { version = "1" } + +# Mocking Market calls +wiremock = { version = "0.5" } + +# (De)Serialization +serde = { version = "^1.0", features = ["derive"] } + +# Logging +log = "0.4" +env_logger = { version = "0.9" } diff --git a/adview-manager/serve/src/main.rs b/adview-manager/serve/src/main.rs new file mode 100644 index 000000000..714503d14 --- /dev/null +++ b/adview-manager/serve/src/main.rs @@ -0,0 +1,152 @@ +use adex_primitives::{ + supermarket::units_for_slot, + targeting::{input::Global, Input}, + test_util::{DUMMY_CAMPAIGN, DUMMY_IPFS}, + ToHex, +}; +use adview_manager::{get_unit_html_with_events, Manager, Options, Size}; +use chrono::Utc; +use log::{debug, info}; +use warp::Filter; +use wiremock::{ + matchers::{method, path, query_param}, + Mock, MockServer, ResponseTemplate, +}; + + +use tera::{Tera, Context}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + env_logger::init(); + + let serve_dir = match std::env::current_dir().unwrap() { + serve_path if serve_path.ends_with("serve") => serve_path, + adview_manager_path if adview_manager_path.ends_with("adview-manager") => adview_manager_path.join("serve"), + // running from the Validator stack workspace + workspace_path => workspace_path.join("adview-manager/serve") + }; + + let templates_glob = format!("{}/templates/**/*.html", serve_dir.display()); + + info!("Tera templates glob path: {templates_glob}"); + // Use globbing + let tera = Tera::new(&templates_glob)?; + + // `GET /ad` + let get_ad = warp::get().and(warp::path("ad")).then(move || { + let tera = tera.clone(); + + async move { + // let logger = logger.clone(); + // For mocking the `get_market_demand_resp` call + let mock_server = MockServer::start().await; + + let market_url = mock_server.uri().parse().unwrap(); + let whitelisted_tokens = vec!["0x6B175474E89094C44Da98b954EedeAC495271d0F" + .parse() + .expect("Valid token Address")]; + let disabled_video = false; + let publisher_addr = "0x0000000000000000626f62627973686d75726461" + .parse() + .unwrap(); + + let options = Options { + market_url, + market_slot: DUMMY_IPFS[0], + publisher_addr, + // All passed tokens must be of the same price and decimals, so that the amounts can be accurately compared + whitelisted_tokens, + size: Some(Size::new(300, 100)), + // TODO: Check this value + navigator_language: Some("bg".into()), + /// Defaulted + disabled_video, + disabled_sticky: false, + }; + + let manager = Manager::new(options.clone(), Default::default()) + .expect("Failed to create Adview Manager"); + let pub_prefix = publisher_addr.to_hex(); + + let units_for_slot_resp = units_for_slot::response::Response { + targeting_input_base: Input { + ad_view: None, + global: Global { + ad_slot_id: options.market_slot.to_string(), + ad_slot_type: "".into(), + publisher_id: publisher_addr, + country: Some("Bulgaria".into()), + event_type: "IMPRESSION".into(), + seconds_since_epoch: Utc::now(), + user_agent_os: None, + user_agent_browser_family: None, + }, + campaign: None, + balances: None, + ad_unit_id: None, + ad_slot: None, + }, + accepted_referrers: vec![], + fallback_unit: None, + campaigns: vec![], + }; + + // Mock the `get_market_demand_resp` call + let mock_call = Mock::given(method("GET")) + // &depositAsset={}&depositAsset={} + .and(path(format!("units-for-slot/{}", options.market_slot))) + // pubPrefix=HEX&depositAsset=0xASSET1&depositAsset=0xASSET2 + .and(query_param("pubPrefix", pub_prefix)) + .and(query_param( + "depositAsset", + "0x6B175474E89094C44Da98b954EedeAC495271d0F", + )) + // .and(query_param("depositAsset[]", "0x6B175474E89094C44Da98b954EedeAC495271d03")) + .respond_with(ResponseTemplate::new(200).set_body_json(units_for_slot_resp)) + .expect(1) + .named("get_market_demand_resp"); + + // Mounting the mock on the mock server - it's now effective! + mock_call.mount(&mock_server).await; + + let demand_resp = manager + .get_market_demand_resp() + .await + .expect("Should return Mocked response"); + + debug!("Mocked response: {demand_resp:?}"); + + let supermarket_ad_unit = + adex_primitives::supermarket::units_for_slot::response::AdUnit { + /// Same as `ipfs` + id: DUMMY_IPFS[1], + media_url: "ipfs://QmcUVX7fvoLMM93uN2bD3wGTH8MXSxeL8hojYfL2Lhp7mR".to_string(), + media_mime: "image/jpeg".to_string(), + target_url: "https://www.adex.network/?stremio-test-banner-1".to_string(), + }; + + let code = get_unit_html_with_events( + &options, + &supermarket_ad_unit, + "localhost", + DUMMY_CAMPAIGN.id, + &DUMMY_CAMPAIGN.validators, + false, + ); + + let html = { + let mut context = Context::new(); + context.insert("ad_code", &code); + + tera.render("ad.html", &context).expect("Should render") + }; + + warp::reply::html(html) + } + }); + + warp::serve(get_ad).run(([127, 0, 0, 1], 3030)).await; + + Ok(()) +} diff --git a/adview-manager/serve/templates/ad.html b/adview-manager/serve/templates/ad.html new file mode 100644 index 000000000..96698ded4 --- /dev/null +++ b/adview-manager/serve/templates/ad.html @@ -0,0 +1,15 @@ + + + + + + + + Serve an Ad + + + + {{ ad_code | safe }} + + + \ No newline at end of file diff --git a/adview-manager/src/lib.rs b/adview-manager/src/lib.rs index b418c23b5..b1506f22b 100644 --- a/adview-manager/src/lib.rs +++ b/adview-manager/src/lib.rs @@ -8,16 +8,17 @@ use adex_primitives::{ supermarket::units_for_slot, supermarket::units_for_slot::response::{AdUnit, Campaign}, targeting::{self, input}, + util::ApiUrl, Address, BigNum, CampaignId, ToHex, UnifiedNum, IPFS, }; use async_std::{sync::RwLock, task::block_on}; use chrono::{DateTime, Duration, Utc}; +use log::error; use num_integer::Integer; use once_cell::sync::Lazy; use rand::Rng; use reqwest::StatusCode; use serde::{Deserialize, Serialize}; -use slog::{error, Logger}; use std::{ cmp::Ordering, collections::VecDeque, @@ -25,12 +26,16 @@ use std::{ sync::Arc, }; use thiserror::Error; -use url::Url; + +pub use url::Url; const IPFS_GATEWAY: &str = "https://ipfs.moonicorn.network/ipfs/"; -// How much time to wait before sending out an impression event -// Related: , , +/// How much time to wait before sending out an impression event (in milliseconds) +/// +/// Related: , , +/// +/// Used for JS [setTimeout](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout) function const WAIT_FOR_IMPRESSION: u32 = 8000; // The number of impressions (won auctions) kept in history const HISTORY_LIMIT: u32 = 50; @@ -38,34 +43,41 @@ const HISTORY_LIMIT: u32 = 50; /// Impression "stickiness" time: see /// 4 minutes allows ~4 campaigns to rotate, considering a default frequency cap of 15 minutes pub static IMPRESSION_STICKINESS_TIME: Lazy = - Lazy::new(|| Duration::milliseconds(240000)); + Lazy::new(|| Duration::milliseconds(240_000)); + +// AdSlot size `width x height` in `pixels` (`px`) +#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] +pub struct Size { + pub width: u64, + pub height: u64, +} + +impl Size { + pub fn new(width: u64, height: u64) -> Self { + Self { width, height } + } +} -#[derive(Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] // TODO: Add Default Ops somehow? pub struct Options { // Defaulted via defaultOpts #[serde(rename = "marketURL")] - pub market_url: Url, + pub market_url: ApiUrl, pub market_slot: IPFS, pub publisher_addr: Address, // All passed tokens must be of the same price and decimals, so that the amounts can be accurately compared - pub whitelisted_tokens: Vec, - pub width: Option, - pub height: Option, + pub whitelisted_tokens: Vec
, + pub size: Option, pub navigator_language: Option, /// Defaulted + #[serde(default)] pub disabled_video: bool, + #[serde(default)] pub disabled_sticky: bool, } -impl Options { - pub fn size(&self) -> Option<(u64, u64)> { - self.width - .and_then(|width| self.height.map(|height| (width, height))) - } -} - #[derive(Debug, Clone)] pub struct HistoryEntry { time: DateTime, @@ -87,33 +99,23 @@ fn normalize_url(url: &str) -> String { } } -fn image_html(on_load: &str, size: &Option<(u64, u64)>, image_url: &str) -> String { +fn image_html(on_load: &str, size: Option, image_url: &str) -> String { let size = size - .map(|(width, height)| format!("width=\"{}\" height=\"{}\"", width, height)) - .unwrap_or_else(|| "".to_string()); + .map(|Size { width, height }| format!("width=\"{width}\" height=\"{height}\"")) + .unwrap_or_default(); - format!("\"AdEx", - image_url = image_url, on_load = on_load, size = size) + format!("\"AdEx") } -fn video_html( - on_load: &str, - size: &Option<(u64, u64)>, - image_url: &str, - media_mime: &str, -) -> String { +fn video_html(on_load: &str, size: Option, image_url: &str, media_mime: &str) -> String { let size = size - .map(|(width, height)| format!("width=\"{}\" height=\"{}\"", width, height)) - .unwrap_or_else(|| "".to_string()); + .map(|Size { width, height }| format!("width=\"{width}\" height=\"{height}\"")) + .unwrap_or_default(); format!( "", - size = size, - on_load = on_load, - image_url = image_url, - media_mime = media_mime ) } @@ -152,18 +154,22 @@ fn randomized_sort_pos(ad_unit: &AdUnit, seed: BigNum) -> BigNum { } fn get_unit_html( - size: &Option<(u64, u64)>, + size: Option, ad_unit: &AdUnit, hostname: &str, on_load: &str, on_click: &str, ) -> String { + // replace all `"` quotes with a single quote `'` + // these values are used inside `onclick` & `onload` html attributes + let on_load = on_load.replace("\"", "'"); + let on_click = on_click.replace("\"", "'"); let image_url = normalize_url(&ad_unit.media_url); let element_html = if is_video(ad_unit) { - video_html(on_load, size, &image_url, &ad_unit.media_mime) + video_html(&on_load, size, &image_url, &ad_unit.media_mime) } else { - image_html(on_load, size, &image_url) + image_html(&on_load, size, &image_url) }; // @TODO click protection page @@ -172,25 +178,22 @@ fn get_unit_html( &format!("utm_source=AdEx+({hostname})", hostname = hostname), ); - let max_min_size = match size { - Some((width, height)) => { + let max_min_size = size + .map(|Size { width, height }| { format!( - "max-width: {}px; min-width: {min_width}px; height: {}px;", - width, - height, + "max-width: {width}px; min-width: {min_width}px; height: {height}px;", // u64 / 2 will floor the result! min_width = width / 2 ) - } - None => String::new(), - }; + }) + .unwrap_or_default(); format!("
{element_html} {adex_icon} -
", style=max_min_size, adex_icon=adex_icon(), final_target_url=final_target_url, on_click = on_click, element_html=element_html) + ", style=max_min_size, adex_icon=adex_icon()) } pub fn get_unit_html_with_events( @@ -222,6 +225,7 @@ pub fn get_unit_html_with_events( let body = serde_json::to_string(&event_body).expect("It should always serialize EventBody"); + // TODO: check whether the JSON body with `''` quotes executes correctly! let fetch_opts = format!("var fetchOpts = {{ method: 'POST', headers: {{ 'content-type': 'application/json' }}, body: {} }};", body); let validators: String = validators @@ -235,9 +239,9 @@ pub fn get_unit_html_with_events( format!("fetch('{}', fetchOpts)", fetch_url) }) .collect::>() - .join(";"); + .join("; "); - format!("{}{}", fetch_opts, validators) + format!("{fetch_opts} {validators}") }; let get_timeout_code = |event_type: &str| -> String { @@ -255,7 +259,7 @@ pub fn get_unit_html_with_events( }; get_unit_html( - &options.size(), + options.size, ad_unit, hostname, &on_load, @@ -266,7 +270,7 @@ pub fn get_unit_html_with_events( #[derive(Debug, Error)] pub enum Error { #[error("Request to the Market failed: status {status} at url {url}")] - Market { status: StatusCode, url: String }, + Market { status: StatusCode, url: Url }, #[error(transparent)] Request(#[from] reqwest::Error), } @@ -277,22 +281,16 @@ pub struct Manager { /// It always trims to HISTORY_LIMIT, removing the oldest (firstly inserted) elements from the History history: Arc>>, client: reqwest::Client, - logger: Logger, } impl Manager { - pub fn new( - options: Options, - history: VecDeque, - logger: Logger, - ) -> Result { + pub fn new(options: Options, history: VecDeque) -> Result { let client = reqwest::Client::builder().build()?; Ok(Self { options, history: Arc::new(RwLock::new(history)), client, - logger, }) } @@ -405,23 +403,24 @@ impl Manager { .collect::>() .join("&"); - // Url adds a trailing `/` - let url = format!( - "{}units-for-slot/{}?pubPrefix={}&{}", - self.options.market_url, self.options.market_slot, pub_prefix, deposit_asset - ); - - let market_response = self.client.get(&url).send().await?; - - if market_response.status() != StatusCode::OK { - Err(Error::Market { + // ApiUrl handles endpoint path (with or without `/`) + let url = self + .options + .market_url + .join(&format!( + "units-for-slot/{ad_slot}?pubPrefix={pub_prefix}&{deposit_asset}", + ad_slot = self.options.market_slot + )) + .expect("Valid URL endpoint!"); + + let market_response = self.client.get(url.clone()).send().await?; + + match market_response.status() { + StatusCode::OK => Ok(market_response.json().await?), + _ => Err(Error::Market { status: market_response.status(), url, - }) - } else { - let units_for_slot_response = market_response.json().await?; - - Ok(units_for_slot_response) + }), } } @@ -482,7 +481,7 @@ impl Manager { .collect(), }; - let on_type_error = |error, rule| error!(&self.logger, "Rule evaluation error for {:?}", campaign_id; "error" => ?error, "rule" => ?rule); + let on_type_error = |type_error, rule| error!(target: "rule-evaluation", "Rule evaluation error for {campaign_id:?}, {rule:?} with error: {type_error:?}"); targeting::eval_with_callback( &m_campaign.campaign.targeting_rules, @@ -562,7 +561,7 @@ impl Manager { html, })) } else if let Some(fallback_unit) = fallback_unit { - let html = get_unit_html(&self.options.size(), &fallback_unit, &hostname, "", ""); + let html = get_unit_html(self.options.size, &fallback_unit, &hostname, "", ""); Ok(Some(NextAdUnit { unit: fallback_unit, price: 0.into(), diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 4b044dfdd..890a53848 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -1,6 +1,8 @@ #![deny(rust_2018_idioms)] #![deny(clippy::all)] #![cfg_attr(docsrs, feature(doc_cfg))] +// TODO: Remove once stabled and upstream num::Integer::div_floor(...) is fixed +#[allow(unstable_name_collisions)] use std::{error, fmt};