feat: fulltext search with meillisearch; deploy in devenv & CI; index product #56
17 changed files with 567 additions and 5 deletions
|
@ -7,6 +7,8 @@ steps:
|
||||||
- DATABASE_URL=postgres://postgres:password@database:5432/postgres
|
- DATABASE_URL=postgres://postgres:password@database:5432/postgres
|
||||||
- VANIKAM_email_URL=smtp://admin:password@email:10025
|
- VANIKAM_email_URL=smtp://admin:password@email:10025
|
||||||
- MAILDEV_URL=http://email:1080
|
- MAILDEV_URL=http://email:1080
|
||||||
|
- VANIKAM_meili_API_KEY=5c8eb5f46c148884fb64da09be211a18347fbba24435ca603adc9eba608ba66d
|
||||||
|
- VANIKAM_meili_url=http://meiliserach:7700
|
||||||
commands:
|
commands:
|
||||||
# - curl -fsSL https://deb.nodesource.com/setup_16.x | bash - &&\
|
# - curl -fsSL https://deb.nodesource.com/setup_16.x | bash - &&\
|
||||||
# - apt update && apt-get -y --no-install-recommends install nodejs tar gpg curl wget
|
# - apt update && apt-get -y --no-install-recommends install nodejs tar gpg curl wget
|
||||||
|
@ -29,7 +31,8 @@ steps:
|
||||||
settings:
|
settings:
|
||||||
dry_run: true
|
dry_run: true
|
||||||
repo: libresolutions/vanikam
|
repo: libresolutions/vanikam
|
||||||
tags: latest
|
tags:
|
||||||
|
latest
|
||||||
|
|
||||||
# build_and_publish_docker_img:
|
# build_and_publish_docker_img:
|
||||||
# image: plugins/docker
|
# image: plugins/docker
|
||||||
|
@ -77,3 +80,9 @@ services:
|
||||||
- MAILDEV_SMTP_PORT=10025
|
- MAILDEV_SMTP_PORT=10025
|
||||||
- MAILDEV_INCOMING_USER=admin
|
- MAILDEV_INCOMING_USER=admin
|
||||||
- MAILDEV_INCOMING_PASS=password
|
- MAILDEV_INCOMING_PASS=password
|
||||||
|
|
||||||
|
meiliserach:
|
||||||
|
image: getmeili/meilisearch:v1.9
|
||||||
|
environment:
|
||||||
|
- MEILI_ENV=development
|
||||||
|
- MEILI_MASTER_KEY=5c8eb5f46c148884fb64da09be211a18347fbba24435ca603adc9eba608ba66d
|
||||||
|
|
182
Cargo.lock
generated
182
Cargo.lock
generated
|
@ -1371,8 +1371,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi",
|
||||||
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1647,6 +1649,7 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls",
|
"tokio-rustls",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
|
"webpki-roots 0.26.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1792,6 +1795,15 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iso8601"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "924e5d73ea28f59011fec52a0d12185d496a9b075d360657aed2a5707f701153"
|
||||||
|
dependencies = [
|
||||||
|
"nom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
|
@ -1836,6 +1848,19 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jsonwebtoken"
|
||||||
|
version = "9.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.21.7",
|
||||||
|
"js-sys",
|
||||||
|
"ring",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "language-tags"
|
name = "language-tags"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
|
@ -1990,6 +2015,46 @@ dependencies = [
|
||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "meilisearch-index-setting-macro"
|
||||||
|
version = "0.27.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "54abc57ee746d0f8c2a40ca900af67cbf022b0e4be3d2ff806939322b5f3bc39"
|
||||||
|
dependencies = [
|
||||||
|
"convert_case 0.6.0",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"structmeta",
|
||||||
|
"syn 2.0.63",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "meilisearch-sdk"
|
||||||
|
version = "0.27.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8d5ded866ed900150a707bd923f2d178ff64a0981d2306c823e8d8003d95ef2"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"bytes",
|
||||||
|
"either",
|
||||||
|
"futures",
|
||||||
|
"futures-io",
|
||||||
|
"iso8601",
|
||||||
|
"jsonwebtoken",
|
||||||
|
"log",
|
||||||
|
"meilisearch-index-setting-macro",
|
||||||
|
"pin-project-lite",
|
||||||
|
"reqwest",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"time",
|
||||||
|
"uuid",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
"web-sys",
|
||||||
|
"yaup",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.2"
|
version = "2.7.2"
|
||||||
|
@ -2592,6 +2657,53 @@ dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quinn"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e4ceeeeabace7857413798eb1ffa1e9c905a9946a57d81fb69b4b71c4d8eb3ad"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"pin-project-lite",
|
||||||
|
"quinn-proto",
|
||||||
|
"quinn-udp",
|
||||||
|
"rustc-hash",
|
||||||
|
"rustls 0.23.6",
|
||||||
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quinn-proto"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ddf517c03a109db8100448a4be38d498df8a210a99fe0e1b9eaf39e78c640efe"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"rand",
|
||||||
|
"ring",
|
||||||
|
"rustc-hash",
|
||||||
|
"rustls 0.23.6",
|
||||||
|
"slab",
|
||||||
|
"thiserror",
|
||||||
|
"tinyvec",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quinn-udp"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9096629c45860fc7fb143e125eb826b5e721e10be3263160c7d60ca832cf8c46"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
"socket2",
|
||||||
|
"tracing",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.36"
|
version = "1.0.36"
|
||||||
|
@ -2717,7 +2829,10 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"quinn",
|
||||||
|
"rustls 0.23.6",
|
||||||
"rustls-pemfile 2.1.2",
|
"rustls-pemfile 2.1.2",
|
||||||
|
"rustls-pki-types",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
|
@ -2725,11 +2840,15 @@ dependencies = [
|
||||||
"system-configuration",
|
"system-configuration",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-native-tls",
|
"tokio-native-tls",
|
||||||
|
"tokio-rustls",
|
||||||
|
"tokio-util",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"url",
|
"url",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
|
"wasm-streams",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
|
"webpki-roots 0.26.1",
|
||||||
"winreg",
|
"winreg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -2842,6 +2961,12 @@ version = "0.1.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -3442,6 +3567,29 @@ version = "0.11.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "structmeta"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2e1575d8d40908d70f6fd05537266b90ae71b15dbbe7a8b7dffa2b759306d329"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"structmeta-derive",
|
||||||
|
"syn 2.0.63",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "structmeta-derive"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.63",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
|
@ -3559,18 +3707,18 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.60"
|
version = "1.0.62"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18"
|
checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.60"
|
version = "1.0.62"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524"
|
checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -3962,6 +4110,7 @@ checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"serde",
|
"serde",
|
||||||
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -4053,6 +4202,7 @@ dependencies = [
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"lettre",
|
"lettre",
|
||||||
"log",
|
"log",
|
||||||
|
"meilisearch-sdk",
|
||||||
"mockall",
|
"mockall",
|
||||||
"postgres-es",
|
"postgres-es",
|
||||||
"pretty_env_logger",
|
"pretty_env_logger",
|
||||||
|
@ -4180,6 +4330,19 @@ version = "0.2.92"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
|
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-streams"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129"
|
||||||
|
dependencies = [
|
||||||
|
"futures-util",
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.69"
|
version = "0.3.69"
|
||||||
|
@ -4422,6 +4585,17 @@ dependencies = [
|
||||||
"linked-hash-map",
|
"linked-hash-map",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yaup"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0144f1a16a199846cb21024da74edd930b43443463292f536b7110b4855b5c6"
|
||||||
|
dependencies = [
|
||||||
|
"form_urlencoded",
|
||||||
|
"serde",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.7.34"
|
version = "0.7.34"
|
||||||
|
|
|
@ -21,6 +21,7 @@ derive_builder = "0.20.0"
|
||||||
derive_more = "0.99.17"
|
derive_more = "0.99.17"
|
||||||
lettre = { version = "0.11.7", features = ["tokio1-rustls-tls", "tracing", "dkim", "tokio1-native-tls", "smtp-transport", "pool", "builder"], default-features = false }
|
lettre = { version = "0.11.7", features = ["tokio1-rustls-tls", "tracing", "dkim", "tokio1-native-tls", "smtp-transport", "pool", "builder"], default-features = false }
|
||||||
log = "0.4.21"
|
log = "0.4.21"
|
||||||
|
meilisearch-sdk = "0.27.0"
|
||||||
mockall = { version = "0.12.1", features = ["nightly"] }
|
mockall = { version = "0.12.1", features = ["nightly"] }
|
||||||
postgres-es = "0.4.11"
|
postgres-es = "0.4.11"
|
||||||
pretty_env_logger = "0.5.0"
|
pretty_env_logger = "0.5.0"
|
||||||
|
|
|
@ -29,3 +29,7 @@ pool = 4
|
||||||
url="smtps://username:password@smtp.example.com:465"
|
url="smtps://username:password@smtp.example.com:465"
|
||||||
from="Vanikam Info <vanikam@example.com>"
|
from="Vanikam Info <vanikam@example.com>"
|
||||||
reply_to="Vanikam Support <vanikam@example.com>"
|
reply_to="Vanikam Support <vanikam@example.com>"
|
||||||
|
|
||||||
|
[meili]
|
||||||
|
url = "http://localhost:7700"
|
||||||
|
#api_key = ""
|
||||||
|
|
|
@ -16,3 +16,11 @@ services:
|
||||||
network_mode: host
|
network_mode: host
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_PASSWORD: password
|
POSTGRES_PASSWORD: password
|
||||||
|
|
||||||
|
meiliserach:
|
||||||
|
image: getmeili/meilisearch:v1.9
|
||||||
|
ports:
|
||||||
|
- 7700:7700
|
||||||
|
environment:
|
||||||
|
- MEILI_ENV=development
|
||||||
|
- MEILI_MASTER_KEY=5c8eb5f46c148884fb64da09be211a18347fbba24435ca603adc9eba608ba66d
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
use derive_builder::Builder;
|
||||||
|
use derive_getters::Getters;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use super::InventoryFTSMeili;
|
||||||
|
use crate::inventory::application::port::output::full_text_search::{
|
||||||
|
add_product_to_store::*, errors::*,
|
||||||
|
};
|
||||||
|
use crate::inventory::domain::{category_aggregate::*, product_aggregate::*};
|
||||||
|
//use super::errors::*;
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters, Builder,
|
||||||
|
)]
|
||||||
|
pub struct MeiliProduct {
|
||||||
|
name: String,
|
||||||
|
description: Option<String>,
|
||||||
|
image: Option<String>, // string = file_name
|
||||||
|
price: Price,
|
||||||
|
category_name: String,
|
||||||
|
product_id: Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MeiliProduct {
|
||||||
|
pub fn new(product: &Product, category: &Category) -> Self {
|
||||||
|
Self {
|
||||||
|
name: product.name().into(),
|
||||||
|
description: product.description().clone(),
|
||||||
|
image: product.image().clone(),
|
||||||
|
price: product.price().clone(),
|
||||||
|
category_name: category.name().into(),
|
||||||
|
product_id: *product.product_id(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl AddProductToStoreFTSPort for InventoryFTSMeili {
|
||||||
|
async fn add_product_to_store(
|
||||||
|
&self,
|
||||||
|
product: &Product,
|
||||||
|
category: &Category,
|
||||||
|
) -> InventoryFTSResult<()> {
|
||||||
|
let store_index = self.client.index(category.store_id().to_string());
|
||||||
|
let meili_product = MeiliProduct::new(product, category);
|
||||||
|
store_index
|
||||||
|
.add_documents(&[meili_product], Some("product_id"))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::types::quantity::Quantity;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_meili() {
|
||||||
|
let settings = crate::settings::tests::get_settings().await;
|
||||||
|
let fts = InventoryFTSMeili::new(&settings.meili.url, &settings.meili.api_key);
|
||||||
|
|
||||||
|
let category = Category::default();
|
||||||
|
let product = ProductBuilder::default()
|
||||||
|
.name("test_meili_product".into())
|
||||||
|
.description(Some("this is a test product".into()))
|
||||||
|
.image(None)
|
||||||
|
.price(
|
||||||
|
PriceBuilder::default()
|
||||||
|
.major(100)
|
||||||
|
.minor(0)
|
||||||
|
.currency(Currency::INR)
|
||||||
|
.build()
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.quantity(Quantity::default())
|
||||||
|
.sku_able(false)
|
||||||
|
.deleted(false)
|
||||||
|
.category_id(*category.category_id())
|
||||||
|
.product_id(Uuid::new_v4())
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
fts.add_product_to_store(&product, &category).await.unwrap();
|
||||||
|
}
|
||||||
|
}
|
19
src/inventory/adapters/output/full_text_search/meili/mod.rs
Normal file
19
src/inventory/adapters/output/full_text_search/meili/mod.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
use meilisearch_sdk::client::*;
|
||||||
|
|
||||||
|
mod add_product_to_store;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct InventoryFTSMeili {
|
||||||
|
client: Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InventoryFTSMeili {
|
||||||
|
pub fn new(meili_url: &str, api_key: &str) -> Self {
|
||||||
|
let client = Client::new(meili_url, Some(api_key)).unwrap();
|
||||||
|
Self { client }
|
||||||
|
}
|
||||||
|
}
|
5
src/inventory/adapters/output/full_text_search/mod.rs
Normal file
5
src/inventory/adapters/output/full_text_search/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
mod meili;
|
97
src/inventory/adapters/output/meili/add_product_to_store.rs
Normal file
97
src/inventory/adapters/output/meili/add_product_to_store.rs
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
use derive_builder::Builder;
|
||||||
|
use derive_getters::Getters;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use super::InventoryFTSMeili;
|
||||||
|
use crate::inventory::application::port::output::full_text_search::{
|
||||||
|
add_product_to_store::*, errors::*,
|
||||||
|
};
|
||||||
|
use crate::inventory::domain::{category_aggregate::*, product_aggregate::*};
|
||||||
|
//use super::errors::*;
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone, Debug, Default, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters, Builder,
|
||||||
|
)]
|
||||||
|
pub struct MeiliProduct {
|
||||||
|
name: String,
|
||||||
|
description: Option<String>,
|
||||||
|
image: Option<String>, // string = file_name
|
||||||
|
price: Price,
|
||||||
|
category_name: String,
|
||||||
|
product_id: Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MeiliProduct {
|
||||||
|
pub fn new(product: &Product, category: &Category) -> Self {
|
||||||
|
Self {
|
||||||
|
name: product.name().into(),
|
||||||
|
description: product.description().clone(),
|
||||||
|
image: product.image().clone(),
|
||||||
|
price: product.price().clone(),
|
||||||
|
category_name: category.name().into(),
|
||||||
|
product_id: *product.product_id(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl AddProductToStoreFTSPort for InventoryFTSMeili {
|
||||||
|
async fn add_product_to_store(
|
||||||
|
&self,
|
||||||
|
product: &Product,
|
||||||
|
category: &Category,
|
||||||
|
) -> InventoryFTSResult<()> {
|
||||||
|
let store_index = self.client.index(category.store_id().to_string());
|
||||||
|
let meili_product = MeiliProduct::new(product, category);
|
||||||
|
store_index
|
||||||
|
.add_documents(&[meili_product], Some("product_id"))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::types::quantity::Quantity;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_meili() {
|
||||||
|
let settings = crate::settings::tests::get_settings().await;
|
||||||
|
let fts = InventoryFTSMeili::new(&settings.meili.url, &settings.meili.api_key);
|
||||||
|
|
||||||
|
let category = Category::default();
|
||||||
|
let product = ProductBuilder::default()
|
||||||
|
.name("test_meili_product".into())
|
||||||
|
.description(Some("this is a test product".into()))
|
||||||
|
.image(None)
|
||||||
|
.price(
|
||||||
|
PriceBuilder::default()
|
||||||
|
.major(100)
|
||||||
|
.minor(0)
|
||||||
|
.currency(Currency::INR)
|
||||||
|
.build()
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.quantity(Quantity::default())
|
||||||
|
.sku_able(false)
|
||||||
|
.deleted(false)
|
||||||
|
.category_id(*category.category_id())
|
||||||
|
.product_id(Uuid::new_v4())
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
fts.add_product_to_store(&product, &category).await.unwrap();
|
||||||
|
}
|
||||||
|
}
|
19
src/inventory/adapters/output/meili/mod.rs
Normal file
19
src/inventory/adapters/output/meili/mod.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
use meilisearch_sdk::client::*;
|
||||||
|
|
||||||
|
mod add_product_to_store;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct InventoryFTSMeili {
|
||||||
|
client: Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InventoryFTSMeili {
|
||||||
|
pub fn new(meili_url: &str, api_key: &str) -> Self {
|
||||||
|
let client = Client::new(meili_url, Some(api_key)).unwrap();
|
||||||
|
Self { client }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
use mockall::predicate::*;
|
||||||
|
use mockall::*;
|
||||||
|
|
||||||
|
use super::errors::*;
|
||||||
|
#[cfg(test)]
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
pub use tests::*;
|
||||||
|
|
||||||
|
use crate::inventory::domain::{category_aggregate::*, product_aggregate::*};
|
||||||
|
|
||||||
|
#[automock]
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait AddProductToStoreFTSPort: Send + Sync {
|
||||||
|
async fn add_product_to_store(
|
||||||
|
&self,
|
||||||
|
product: &Product,
|
||||||
|
cateogry: &Category,
|
||||||
|
) -> InventoryFTSResult<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type AddProductToStoreFTSPortObj = std::sync::Arc<dyn AddProductToStoreFTSPort>;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub fn mock_add_product_to_store_fts_port(times: Option<usize>) -> AddProductToStoreFTSPortObj {
|
||||||
|
let mut m = MockAddProductToStoreFTSPort::new();
|
||||||
|
if let Some(times) = times {
|
||||||
|
m.expect_add_product_to_store()
|
||||||
|
.times(times)
|
||||||
|
.returning(|_, _| Ok(()));
|
||||||
|
} else {
|
||||||
|
m.expect_add_product_to_store().returning(|_, _| Ok(()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Arc::new(m)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
use derive_more::Display;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
pub type InventoryFTSResult<V> = Result<V, InventoryFTSError>;
|
||||||
|
|
||||||
|
#[derive(Debug, Display, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub enum InventoryFTSError {}
|
|
@ -0,0 +1,7 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
pub mod add_product_to_store;
|
||||||
|
pub mod errors;
|
||||||
|
pub mod update_product;
|
|
@ -0,0 +1,3 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
@ -3,3 +3,4 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
pub mod db;
|
pub mod db;
|
||||||
|
pub mod full_text_search;
|
||||||
|
|
58
src/settings/meili.rs
Normal file
58
src/settings/meili.rs
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
use config::{builder::DefaultState, ConfigBuilder};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Validate, Deserialize, Eq, PartialEq)]
|
||||||
|
pub struct Meili {
|
||||||
|
#[validate(url)]
|
||||||
|
pub url: String,
|
||||||
|
pub api_key: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Meili {
|
||||||
|
pub fn env_override(mut s: ConfigBuilder<DefaultState>) -> ConfigBuilder<DefaultState> {
|
||||||
|
for (parameter, env_var_name) in [
|
||||||
|
("meili.url", "VANIKAM_meili_URL"),
|
||||||
|
("meili.api_key", "VANIKAM_meili_API_KEY"),
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
{
|
||||||
|
if let Ok(val) = env::var(env_var_name) {
|
||||||
|
log::debug!("Overriding [{parameter}] with environment variable {env_var_name}");
|
||||||
|
s = s.set_override(parameter, val).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::env_helper;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_meili_env_override() {
|
||||||
|
let init_settings = crate::settings::Settings::new().unwrap();
|
||||||
|
env_helper!(
|
||||||
|
init_settings,
|
||||||
|
"VANIKAM_meili_URL",
|
||||||
|
"https://test_meili_env_override.org",
|
||||||
|
meili.url
|
||||||
|
);
|
||||||
|
env_helper!(
|
||||||
|
init_settings,
|
||||||
|
"VANIKAM_meili_API_KEY",
|
||||||
|
"apitestketkere",
|
||||||
|
meili.api_key
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,10 +13,12 @@ use validator::Validate;
|
||||||
|
|
||||||
pub mod database;
|
pub mod database;
|
||||||
pub mod email;
|
pub mod email;
|
||||||
|
pub mod meili;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
|
|
||||||
use database::{DBType, Database};
|
use database::{DBType, Database};
|
||||||
use email::Email;
|
use email::Email;
|
||||||
|
use meili::Meili;
|
||||||
use server::Server;
|
use server::Server;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Validate, Deserialize, Eq, PartialEq)]
|
#[derive(Debug, Clone, Validate, Deserialize, Eq, PartialEq)]
|
||||||
|
@ -29,6 +31,7 @@ pub struct Settings {
|
||||||
pub database: Database,
|
pub database: Database,
|
||||||
pub server: Server,
|
pub server: Server,
|
||||||
pub email: Email,
|
pub email: Email,
|
||||||
|
pub meili: Meili,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Settings {
|
impl Settings {
|
||||||
|
@ -48,6 +51,7 @@ impl Settings {
|
||||||
|
|
||||||
s = Database::env_override(s);
|
s = Database::env_override(s);
|
||||||
s = Email::env_override(s);
|
s = Email::env_override(s);
|
||||||
|
s = Meili::env_override(s);
|
||||||
Server::env_override(s)
|
Server::env_override(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue